From 817b82f76f279bccf217145ef0116f16468dc187 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 8 Dec 2020 11:45:43 +0000 Subject: [PATCH 01/65] Increase popup size for small lcd --- radio/src/gui/128x64/popups.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/128x64/popups.h b/radio/src/gui/128x64/popups.h index e5d483e0b..9400d69f7 100644 --- a/radio/src/gui/128x64/popups.h +++ b/radio/src/gui/128x64/popups.h @@ -23,16 +23,16 @@ #include -#define MESSAGEBOX_X 10 +#define MESSAGEBOX_X 1 #define MESSAGEBOX_Y 16 -#define MESSAGEBOX_W (LCD_W - 19) +#define MESSAGEBOX_W (LCD_W - 2) #define MENU_X MESSAGEBOX_X #define MENU_Y MESSAGEBOX_Y #define MENU_W MESSAGEBOX_W #define WARNING_LINE_LEN 20 -#define WARNING_LINE_X 16 +#define WARNING_LINE_X 3 #define WARNING_LINE_Y 3*FH #define POPUP_MENU_MAX_LINES 12 From a95df473843069ff28d4a3dbcaf35ba36ceed566 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 14 Dec 2020 19:41:14 +0100 Subject: [PATCH 02/65] Fixes #8159 Still not tested, but it doesn't reboot anymore --- radio/src/targets/common/arm/stm32/aux_serial_driver.cpp | 2 +- radio/src/trainer.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp index d88c32610..5c8f1e97f 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -128,7 +128,7 @@ void auxSerialInit(unsigned int mode, unsigned int protocol) break; case UART_MODE_SBUS_TRAINER: - auxSerialSetup(SBUS_BAUDRATE, false, USART_WordLength_9b, USART_Parity_Even, USART_StopBits_2); // 2 stop bits requires USART_WordLength_9b + auxSerialSetup(SBUS_BAUDRATE, true, USART_WordLength_9b, USART_Parity_Even, USART_StopBits_2); // 2 stop bits requires USART_WordLength_9b AUX_SERIAL_POWER_ON(); break; diff --git a/radio/src/trainer.cpp b/radio/src/trainer.cpp index 6c6bc7b79..893d42b67 100644 --- a/radio/src/trainer.cpp +++ b/radio/src/trainer.cpp @@ -140,15 +140,19 @@ void checkTrainerSettings() if (g_eeGeneral.auxSerialMode == UART_MODE_SBUS_TRAINER) auxSerialSbusInit(); else + init_trainer_capture(); + break; #endif #if defined(AUX2_SERIAL) if (g_eeGeneral.aux2SerialMode == UART_MODE_SBUS_TRAINER) aux2SerialSbusInit(); else -#endif init_trainer_capture(); break; #endif + init_trainer_capture(); + break; +#endif case TRAINER_MODE_MASTER_TRAINER_JACK: init_trainer_capture(); From 94e50f146a3abf91caa5d01b8441231f7a930fff Mon Sep 17 00:00:00 2001 From: Hans Christian Olaussen <41271048+klutvott123@users.noreply.github.com> Date: Tue, 15 Dec 2020 22:43:37 +0100 Subject: [PATCH 03/65] Prevent running crossfire.lua if crossfire not available Checks if crossfire is available before running crossfire.lua. Exits the script and shows an error message if not. --- radio/sdcard/horus/SCRIPTS/TOOLS/crossfire.lua | 5 +++++ radio/sdcard/taranis-x7/SCRIPTS/TOOLS/crossfire.lua | 5 +++++ radio/sdcard/taranis-x9/SCRIPTS/TOOLS/crossfire.lua | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/radio/sdcard/horus/SCRIPTS/TOOLS/crossfire.lua b/radio/sdcard/horus/SCRIPTS/TOOLS/crossfire.lua index 0f9b1a20f..b19bb5ee2 100644 --- a/radio/sdcard/horus/SCRIPTS/TOOLS/crossfire.lua +++ b/radio/sdcard/horus/SCRIPTS/TOOLS/crossfire.lua @@ -30,6 +30,11 @@ local function run(event) return 2 end + if crossfireTelemetryPush() == nil then + error("Crossfire not available!") + return 2 + end + chdir("/SCRIPTS/TOOLS/CROSSFIRE") return "crossfire.lua" end diff --git a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/crossfire.lua b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/crossfire.lua index debf9056d..3e8a6930a 100644 --- a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/crossfire.lua +++ b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/crossfire.lua @@ -30,6 +30,11 @@ local function run(event) return 2 end + if crossfireTelemetryPush() == nil then + error("Crossfire not available!") + return 2 + end + chdir("/SCRIPTS/TOOLS/CROSSFIRE") return "crossfire.lua" end diff --git a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/crossfire.lua b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/crossfire.lua index 0f9b1a20f..b19bb5ee2 100644 --- a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/crossfire.lua +++ b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/crossfire.lua @@ -30,6 +30,11 @@ local function run(event) return 2 end + if crossfireTelemetryPush() == nil then + error("Crossfire not available!") + return 2 + end + chdir("/SCRIPTS/TOOLS/CROSSFIRE") return "crossfire.lua" end From d10b16d4c69c3099f84e9cdc238dd3b5a690c2ed Mon Sep 17 00:00:00 2001 From: elecpower Date: Sun, 20 Dec 2020 14:14:21 +1100 Subject: [PATCH 04/65] Dynamic ui data models continued --- .../src/datamodels/rawitemdatamodels.cpp | 269 +++++++++++++++++- companion/src/datamodels/rawitemdatamodels.h | 86 +++++- .../src/firmwares/adjustmentreference.cpp | 7 +- companion/src/firmwares/adjustmentreference.h | 1 + companion/src/firmwares/curvereference.cpp | 6 + companion/src/firmwares/curvereference.h | 1 + .../src/firmwares/customfunctiondata.cpp | 97 +++++-- companion/src/firmwares/customfunctiondata.h | 26 +- companion/src/firmwares/modeldata.cpp | 39 +++ companion/src/firmwares/modeldata.h | 4 + companion/src/firmwares/rawsource.cpp | 32 +-- companion/src/firmwares/rawsource.h | 3 + companion/src/firmwares/sensordata.cpp | 38 +++ companion/src/firmwares/sensordata.h | 3 + 14 files changed, 557 insertions(+), 55 deletions(-) diff --git a/companion/src/datamodels/rawitemdatamodels.cpp b/companion/src/datamodels/rawitemdatamodels.cpp index d7c1b6682..3805b98d6 100644 --- a/companion/src/datamodels/rawitemdatamodels.cpp +++ b/companion/src/datamodels/rawitemdatamodels.cpp @@ -22,6 +22,7 @@ #include "generalsettings.h" #include "eeprominterface.h" #include "modeldata.h" +#include "adjustmentreference.h" RawSourceItemModel::RawSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : AbstractRawItemDataModel(generalSettings, modelData, parent) @@ -187,28 +188,28 @@ void RawSwitchItemModel::update() CurveItemModel::CurveItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : AbstractRawItemDataModel(generalSettings, modelData, parent) { + if (!modelData) + return; + const int count = getCurrentFirmware()->getCapability(NumCurves); for (int i = -count ; i <= count; ++i) { QStandardItem * modelItem = new QStandardItem(); modelItem->setData(i, ItemIdRole); - int flags; - if (i < 0) - flags = DataGroups::NegativeGroup; - else if (i > 0) - flags = DataGroups::PositiveGroup; - else - flags = DataGroups::NoneGroup; + int flags = i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup; + if (i == 0) + flags |= DataGroups::NoneGroup; modelItem->setData(flags, ItemFlagsRole); setDynamicItemData(modelItem, i); appendRow(modelItem); } } -void CurveItemModel::setDynamicItemData(QStandardItem * item, int index) const +void CurveItemModel::setDynamicItemData(QStandardItem * item, const int index) const { - item->setText(CurveReference(CurveReference::CURVE_REF_CUSTOM, index).toString(modelData, false)); - item->setData(true, IsAvailableRole); + CurveReference cr = CurveReference(CurveReference::CURVE_REF_CUSTOM, index); + item->setText(cr.toString(modelData, false)); + item->setData(cr.isAvailable(), IsAvailableRole); } void CurveItemModel::update() @@ -221,16 +222,228 @@ void CurveItemModel::update() emit dataUpdateComplete(); } + // -// CommonItemModels +// GVarReferenceItemModel +// + +GVarReferenceItemModel::GVarReferenceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : + AbstractRawItemDataModel(generalSettings, modelData, parent) +{ + if (!modelData) + return; + + const int count = getCurrentFirmware()->getCapability(Gvars); + if (count > 0) { + addItems(-count); + addItems(count); + } +} + +void GVarReferenceItemModel::addItems(int count) +{ + int i = (count < 0 ? count : 1); + count = (i < 0 ? 0 : count + i); + for ( ; i < count; ++i) { + QStandardItem * modelItem = new QStandardItem(); + AdjustmentReference ar(AdjustmentReference::ADJUST_REF_GVAR, i); + modelItem->setData(ar.toValue(), ItemIdRole); + modelItem->setData(i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup, ItemFlagsRole); + setDynamicItemData(modelItem, ar); + appendRow(modelItem); + } +} + +void GVarReferenceItemModel::setDynamicItemData(QStandardItem * item, const AdjustmentReference & ar) const +{ + item->setText(ar.toString(modelData, false)); + item->setData(ar.isAvailable(), IsAvailableRole); +} + +void GVarReferenceItemModel::update() +{ + emit dataAboutToBeUpdated(); + + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), AdjustmentReference(item(i)->data(ItemIdRole).toInt())); + + emit dataUpdateComplete(); +} + + +// +// ThrottleSourceItemModel +// + +ThrottleSourceItemModel::ThrottleSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : + AbstractRawItemDataModel(generalSettings, modelData, parent) +{ + if (!modelData) + return; + + for (int i = 0; i < modelData->thrTraceSrcCount(); i++) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setData(i, ItemIdRole); + setDynamicItemData(modelItem, i); + appendRow(modelItem); + } +} + +void ThrottleSourceItemModel::setDynamicItemData(QStandardItem * item, const int index) const +{ + item->setText(modelData->thrTraceSrcToString(index)); + item->setData(modelData->isThrTraceSrcAvailable(generalSettings, index), IsAvailableRole); +} + +void ThrottleSourceItemModel::update() +{ + emit dataAboutToBeUpdated(); + + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + + emit dataUpdateComplete(); +} + + +// +// CustomFuncActionItemModel +// + +CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : + AbstractRawItemDataModel(generalSettings, modelData, parent) +{ + for (int i = 0; i < AssignFunc::FuncCount; i++) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setData(i, ItemIdRole); + modelItem->setData(CustomFunctionData::funcContext(i), ItemFlagsRole); + setDynamicItemData(modelItem, i); + appendRow(modelItem); + } +} + +void CustomFuncActionItemModel::setDynamicItemData(QStandardItem * item, const int index) const +{ + item->setText(CustomFunctionData(AssignFunc(index)).funcToString(modelData)); + item->setData(CustomFunctionData::isFuncAvailable(index), IsAvailableRole); +} + +void CustomFuncActionItemModel::update() +{ + emit dataAboutToBeUpdated(); + + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + + emit dataUpdateComplete(); +} + + +// +// CustomFuncResetParamItemModel +// + +CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : + AbstractRawItemDataModel(generalSettings, modelData, parent) +{ + for (int i = 0; i < CustomFunctionData::resetParamCount(modelData); i++) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setData(i, ItemIdRole); + setDynamicItemData(modelItem, i); + appendRow(modelItem); + } +} + +void CustomFuncResetParamItemModel::setDynamicItemData(QStandardItem * item, const int index) const +{ + CustomFunctionData cfd = CustomFunctionData(AssignFunc::FuncReset); + cfd.param = index; + item->setText(cfd.paramToString(modelData)); + item->setData(CustomFunctionData::isResetParamAvailable(modelData, index), IsAvailableRole); +} + +void CustomFuncResetParamItemModel::update() +{ + emit dataAboutToBeUpdated(); + + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + + emit dataUpdateComplete(); +} + + +// +// TelemetrySourceItemModel +// + +TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : + AbstractRawItemDataModel(generalSettings, modelData, parent) +{ + if (!modelData) + return; + + const int count = getCurrentFirmware()->getCapability(Sensors); + + for (int i = -count + 1; i < count; ++i) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setData(i, ItemIdRole); + int flags = i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup; + if (i == 0) + flags |= DataGroups::NoneGroup; + modelItem->setData(flags, ItemFlagsRole); + setDynamicItemData(modelItem, i); + appendRow(modelItem); + } +} + +void TelemetrySourceItemModel::setDynamicItemData(QStandardItem * item, const int index) const +{ + item->setText(SensorData::sourceToString(modelData, index)); + item->setData(SensorData::isSourceAvailable(modelData, index), IsAvailableRole); +} + +void TelemetrySourceItemModel::update() +{ + emit dataAboutToBeUpdated(); + + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + + emit dataUpdateComplete(); +} + + +// +// CommonItemModels TODO rename to DynamicDataModels // CommonItemModels::CommonItemModels(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : QObject(parent) { m_rawSourceItemModel = new RawSourceItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("raw source"), qobject_cast(m_rawSourceItemModel)); + m_rawSwitchItemModel = new RawSwitchItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("raw switch"), qobject_cast(m_rawSwitchItemModel)); + m_curveItemModel = new CurveItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("curve"), qobject_cast(m_curveItemModel)); + + m_gvarReferenceItemModel = new GVarReferenceItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("gvar ref"), qobject_cast(m_gvarReferenceItemModel)); + + m_throttleSourceItemModel = new ThrottleSourceItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("thr source"), qobject_cast(m_throttleSourceItemModel)); + + m_customFuncActionItemModel = new CustomFuncActionItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("cf action"), qobject_cast(m_customFuncActionItemModel)); + + m_customFuncResetParamItemModel = new CustomFuncResetParamItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("cf reset param"), qobject_cast(m_customFuncResetParamItemModel)); + + m_telemetrySourceItemModel = new TelemetrySourceItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("tele source"), qobject_cast(m_telemetrySourceItemModel)); } CommonItemModels::~CommonItemModels() @@ -241,24 +454,54 @@ void CommonItemModels::update(const RadioModelObjects radioModelObjects) { switch (radioModelObjects) { case RMO_CHANNELS: + m_throttleSourceItemModel->update(); + // no break + case RMO_TIMERS: + m_customFuncActionItemModel->update(); + // no break case RMO_INPUTS: case RMO_TELEMETRY_SENSORS: - case RMO_TIMERS: m_rawSourceItemModel->update(); + m_customFuncResetParamItemModel->update(); + m_telemetrySourceItemModel->update(); break; - case RMO_FLIGHT_MODES: + case RMO_GLOBAL_VARIABLES: + m_gvarReferenceItemModel->update(); + m_customFuncActionItemModel->update(); + // no break + case RMO_FLIGHT_MODES: case RMO_LOGICAL_SWITCHES: m_rawSourceItemModel->update(); m_rawSwitchItemModel->update(); break; + case RMO_CURVES: m_curveItemModel->update(); break; + case RMO_SCRIPTS: // no need to refresh break; + default: qDebug() << "Unknown RadioModelObject:" << radioModelObjects; } } + + +void dumpModelContents(QString desc, AbstractRawItemDataModel * dataModel) +{ + if (dataModel) { + qDebug() << "model:" << desc << "rows:" << dataModel->rowCount(); + for (int i = 0; i < dataModel->rowCount(); ++i) { + qDebug() << "row:" << i << "text:" << dataModel->item(i)->data(Qt::DisplayRole).toString() + << "id:" << dataModel->item(i)->data(AbstractRawItemDataModel::ItemIdRole).toInt() + << "avail:" << dataModel->item(i)->data(AbstractRawItemDataModel::IsAvailableRole).toBool() + << "flags:" << dataModel->item(i)->data(AbstractRawItemDataModel::ItemFlagsRole).toInt() + << "type:" << dataModel->item(i)->data(AbstractRawItemDataModel::ItemTypeRole).toInt(); + } + } + else + qDebug() << "model:" << desc << "did not cast"; +} diff --git a/companion/src/datamodels/rawitemdatamodels.h b/companion/src/datamodels/rawitemdatamodels.h index 48cb55fd2..fbcfe8be9 100644 --- a/companion/src/datamodels/rawitemdatamodels.h +++ b/companion/src/datamodels/rawitemdatamodels.h @@ -28,6 +28,7 @@ class GeneralSettings; class ModelData; +class AdjustmentReference; class AbstractRawItemDataModel: public QStandardItemModel { @@ -102,7 +103,78 @@ class CurveItemModel: public AbstractRawItemDataModel void update() override; protected: - void setDynamicItemData(QStandardItem * item, int index) const; + void setDynamicItemData(QStandardItem * item, const int index) const; +}; + + +class GVarReferenceItemModel: public AbstractRawItemDataModel +{ + Q_OBJECT + public: + explicit GVarReferenceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + + public slots: + void update() override; + + protected: + void setDynamicItemData(QStandardItem * item, const AdjustmentReference & ar) const; + void addItems(int count); +}; + + +class ThrottleSourceItemModel: public AbstractRawItemDataModel +{ + Q_OBJECT + public: + explicit ThrottleSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + + public slots: + void update() override; + + protected: + void setDynamicItemData(QStandardItem * item, const int index) const; +}; + + +class CustomFuncActionItemModel: public AbstractRawItemDataModel +{ + Q_OBJECT + public: + explicit CustomFuncActionItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + + public slots: + void update() override; + + protected: + void setDynamicItemData(QStandardItem * item, const int index) const; +}; + + +class CustomFuncResetParamItemModel: public AbstractRawItemDataModel +{ + Q_OBJECT + public: + explicit CustomFuncResetParamItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + + public slots: + void update() override; + + protected: + void setDynamicItemData(QStandardItem * item, const int index) const; +}; + + +class TelemetrySourceItemModel: public AbstractRawItemDataModel +{ + Q_OBJECT + public: + explicit TelemetrySourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + + public slots: + void update() override; + + protected: + void setDynamicItemData(QStandardItem * item, const int index) const; }; @@ -130,11 +202,23 @@ class CommonItemModels: public QObject RawSourceItemModel * rawSourceItemModel() const { return m_rawSourceItemModel; } RawSwitchItemModel * rawSwitchItemModel() const { return m_rawSwitchItemModel; } CurveItemModel * curveItemModel() const { return m_curveItemModel; } + GVarReferenceItemModel * gvarItemModel() const { return m_gvarReferenceItemModel; } + ThrottleSourceItemModel * throttleSourceItemModel() const { return m_throttleSourceItemModel; } + CustomFuncActionItemModel * customFuncActionItemModel() const { return m_customFuncActionItemModel; } + CustomFuncResetParamItemModel * customFuncResetParamItemModel() const { return m_customFuncResetParamItemModel; } + TelemetrySourceItemModel * telemetrySourceItemModel() const { return m_telemetrySourceItemModel; } private: RawSourceItemModel *m_rawSourceItemModel; RawSwitchItemModel *m_rawSwitchItemModel; CurveItemModel *m_curveItemModel; + GVarReferenceItemModel *m_gvarReferenceItemModel; + ThrottleSourceItemModel *m_throttleSourceItemModel; + CustomFuncActionItemModel *m_customFuncActionItemModel; + CustomFuncResetParamItemModel *m_customFuncResetParamItemModel; + TelemetrySourceItemModel *m_telemetrySourceItemModel; }; +void dumpModelContents(QString desc, AbstractRawItemDataModel * dataModel); + #endif // RAWITEMDATAMODELS_H diff --git a/companion/src/firmwares/adjustmentreference.cpp b/companion/src/firmwares/adjustmentreference.cpp index 7181e15dd..ae487effa 100644 --- a/companion/src/firmwares/adjustmentreference.cpp +++ b/companion/src/firmwares/adjustmentreference.cpp @@ -19,7 +19,7 @@ */ #include "adjustmentreference.h" -#include "radiodata.h" // for ModelData +#include "modeldata.h" AdjustmentReference::AdjustmentReference(int value) { @@ -76,3 +76,8 @@ QString AdjustmentReference::toString(const ModelData * model, const bool sign) } return ret; } + +const bool AdjustmentReference::isAvailable() const +{ + return true; +} diff --git a/companion/src/firmwares/adjustmentreference.h b/companion/src/firmwares/adjustmentreference.h index 2a7b7e141..9377cd5e8 100644 --- a/companion/src/firmwares/adjustmentreference.h +++ b/companion/src/firmwares/adjustmentreference.h @@ -45,6 +45,7 @@ class AdjustmentReference { bool isSet() const { return type != ADJUST_REF_VALUE || value != 0; } bool isValid(const int value) const; QString toString(const ModelData * model = nullptr, const bool sign = false) const; + const bool isAvailable() const; inline const int toValue() const { diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 449dc0266..6ee2e38c5 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -63,6 +63,11 @@ const bool CurveReference::isValueNumber() const return (type == CURVE_REF_DIFF || type == CURVE_REF_EXPO) && AdjustmentReference(value).type == AdjustmentReference::ADJUST_REF_VALUE; } +const bool CurveReference::isAvailable() const +{ + return true; +} + // static int CurveReference::getDefaultValue(const CurveRefType type, const bool isGVar) { @@ -101,6 +106,7 @@ QString CurveReference::functionToString(const int value) } /* + * CurveReferenceUIManager */ diff --git a/companion/src/firmwares/curvereference.h b/companion/src/firmwares/curvereference.h index 0a1c7a180..018fb12aa 100644 --- a/companion/src/firmwares/curvereference.h +++ b/companion/src/firmwares/curvereference.h @@ -63,6 +63,7 @@ class CurveReference { const bool isValueNumber() const; const bool isValueReference() const { return !isValueNumber(); } const QString toString(const ModelData * model = nullptr, bool verbose = true) const; + const bool isAvailable() const; CurveRefType type; int value; diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index d6918e27c..d4942c60c 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -38,12 +38,12 @@ bool CustomFunctionData::isEmpty() const QString CustomFunctionData::nameToString(int index, bool globalContext) const { - return RadioData::getElementName((globalContext ? tr("GF") : tr("SF")), index+1, 0, true); + return RadioData::getElementName((globalContext ? tr("GF") : tr("SF")), index + 1, 0, true); } QString CustomFunctionData::funcToString(const ModelData * model) const { - if (func >= FuncOverrideCH1 && func <= FuncOverrideCH32) + if (func >= FuncOverrideCH1 && func <= FuncOverrideCHLast) return tr("Override %1").arg(RawSource(SOURCE_TYPE_CH, func).toString(model)); else if (func == FuncTrainer) return tr("Trainer Sticks"); @@ -65,8 +65,8 @@ QString CustomFunctionData::funcToString(const ModelData * model) const return tr("Haptic"); else if (func == FuncReset) return tr("Reset"); - else if (func >= FuncSetTimer1 && func <= FuncSetTimer3) - return tr("Set Timer %1").arg(func-FuncSetTimer1+1); + else if (func >= FuncSetTimer1 && func <= FuncSetTimerLast) + return tr("Set %1").arg(RawSource(SOURCE_TYPE_SPECIAL, SOURCE_TYPE_SPECIAL_TIMER1_IDX + func - FuncSetTimer1).toString(model)); else if (func == FuncVario) return tr("Vario"); else if (func == FuncPlayPrompt) @@ -90,7 +90,7 @@ QString CustomFunctionData::funcToString(const ModelData * model) const else if (func == FuncBackgroundMusicPause) return tr("Background Music Pause"); else if (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast) - return tr("Adjust %1").arg(RawSource(SOURCE_TYPE_GVAR, func-FuncAdjustGV1).toString(model)); + return tr("Adjust %1").arg(RawSource(SOURCE_TYPE_GVAR, func - FuncAdjustGV1).toString(model)); else if (func == FuncSetFailsafe) return tr("Set Failsafe"); else if (func == FuncRangeCheckInternalModule) @@ -102,7 +102,7 @@ QString CustomFunctionData::funcToString(const ModelData * model) const else if (func == FuncBindExternalModule) return tr("Bind Ext. Module"); else { - return QString("???"); // Highlight unknown functions with output of question marks.(BTW should not happen that we do not know what a function is) + return QString(CPN_STR_UNKNOWN_ITEM); } } @@ -111,11 +111,16 @@ void CustomFunctionData::populateResetParams(const ModelData * model, QComboBox int val = 0; Firmware * firmware = Firmware::getCurrentVariant(); - b->addItem(tr("Timer1"), val++); - b->addItem(tr("Timer2"), val++); - b->addItem( tr("Timer3"), val++); + for (int i = 0; i < CPN_MAX_TIMERS; i++, val++) { + if (i < firmware->getCapability(Timers)) { + RawSource item = RawSource(SOURCE_TYPE_SPECIAL, i + SOURCE_TYPE_SPECIAL_TIMER1_IDX); + b->addItem(item.toString(model), val); + } + } + b->addItem(tr("Flight"), val++); b->addItem(tr("Telemetry"), val++); + int reCount = firmware->getCapability(RotaryEncoders); if (reCount == 1) { b->addItem(tr("Rotary Encoder"), val++); @@ -124,6 +129,7 @@ void CustomFunctionData::populateResetParams(const ModelData * model, QComboBox b->addItem(tr("REa"), val++); b->addItem(tr("REb"), val++); } + if (model) { for (int i = 0; i < firmware->getCapability(Sensors); ++i) { if (model->sensorData[i].isAvailable()) { @@ -132,6 +138,7 @@ void CustomFunctionData::populateResetParams(const ModelData * model, QComboBox } } } + b->setCurrentIndex(b->findData(value)); } @@ -153,18 +160,18 @@ QString CustomFunctionData::paramToString(const ModelData * model) const return QString("%1").arg(param); } else if (func == FuncLogs) { - return QString("%1").arg(param/10.0) + tr("s"); + return QString("%1").arg(param / 10.0) + tr("s"); } else if (func == FuncPlaySound) { CustomFunctionData::populatePlaySoundParams(qs); - if (param>=0 && param<(int)qs.count()) + if (param >= 0 && param < (int)qs.count()) return qs.at(param); else return tr("Inconsistent parameter"); } else if (func == FuncPlayHaptic) { CustomFunctionData::populateHapticParams(qs); - if (param>=0 && param<(int)qs.count()) + if (param >= 0 && param < (int)qs.count()) return qs.at(param); else return tr("Inconsistent parameter"); @@ -193,7 +200,7 @@ QString CustomFunctionData::paramToString(const ModelData * model) const else if (func >= FuncAdjustGV1 && func < FuncCount) { switch (adjustMode) { case FUNC_ADJUST_GVAR_CONSTANT: - return tr("Value ")+QString("%1").arg(param); + return tr("Value ") + QString("%1").arg(param); case FUNC_ADJUST_GVAR_SOURCE: case FUNC_ADJUST_GVAR_GVAR: return RawSource(param).toString(); @@ -218,16 +225,16 @@ QString CustomFunctionData::repeatToString() const } else { unsigned int step = 1; - return tr("repeat(%1s)").arg(step*repeatParam); + return tr("repeat(%1s)").arg(step * repeatParam); } } QString CustomFunctionData::enabledToString() const { - if ((func >= FuncOverrideCH1 && func <= FuncOverrideCH32) || + if ((func >= FuncOverrideCH1 && func <= FuncOverrideCHLast) || (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast) || (func == FuncReset) || - (func >= FuncSetTimer1 && func <= FuncSetTimer2) || + (func >= FuncSetTimer1 && func <= FuncSetTimerLast) || (func == FuncVolume) || (func == FuncBacklight) || (func <= FuncInstantTrim)) { @@ -238,6 +245,64 @@ QString CustomFunctionData::enabledToString() const return ""; } +// static +bool CustomFunctionData::isFuncAvailable(int index) +{ + Firmware * fw = getCurrentFirmware(); + + bool ret = (((index >= FuncOverrideCH1 && index <= FuncOverrideCHLast) && !fw->getCapability(SafetyChannelCustomFunction)) || + ((index == FuncVolume || index == FuncBackgroundMusic || index == FuncBackgroundMusicPause) && !fw->getCapability(HasVolume)) || + ((index == FuncPlayScript && !IS_HORUS_OR_TARANIS(fw->getBoard()))) || + ((index == FuncPlayHaptic) && !fw->getCapability(Haptic)) || + ((index == FuncPlayBoth) && !fw->getCapability(HasBeeper)) || + ((index == FuncLogs) && !fw->getCapability(HasSDLogs)) || + ((index >= FuncSetTimer1 && index <= FuncSetTimerLast) && index > FuncSetTimer1 + fw->getCapability(Timers)) || + ((index == FuncScreenshot) && !IS_HORUS_OR_TARANIS(fw->getBoard())) || + ((index >= FuncRangeCheckInternalModule && index <= FuncBindExternalModule) && !fw->getCapability(DangerousFunctions)) || + ((index >= FuncAdjustGV1 && index <= FuncAdjustGVLast) && !fw->getCapability(Gvars)) + ); + return !ret; +} + +// static +int CustomFunctionData::funcContext(int index) +{ + int ret = AllFunctionContexts; + + if ((index >= FuncOverrideCH1 && index <= FuncOverrideCHLast) || + (index >= FuncRangeCheckInternalModule && index <= FuncBindExternalModule) || + (index >= FuncAdjustGV1 && index <= FuncAdjustGVLast)) + ret &= ~GlobalFunctionsContext; + + return ret; +} +// static +int CustomFunctionData::resetParamCount(const ModelData * model) +{ + QComboBox cb; + CustomFunctionData::populateResetParams(model, &cb); + return cb.count(); +} + +// static +bool CustomFunctionData::isResetParamAvailable(const ModelData * model, int index) +{ + Firmware * firmware = getCurrentFirmware(); + + if (index < CPN_MAX_TIMERS) { + if (index < firmware->getCapability(Timers)) + return true; + else + return false; + } + else if (index < CPN_MAX_TIMERS + firmware->getCapability(RotaryEncoders)) + return true; + else if (model && index < CPN_MAX_TIMERS + firmware->getCapability(RotaryEncoders) + firmware->getCapability(Sensors)) + return model->sensorData[index - CPN_MAX_TIMERS - firmware->getCapability(RotaryEncoders)].isAvailable(); + + return false; +} + void CustomFunctionData::convert(RadioDataConversionState & cstate) { cstate.setComponent(tr("CFN"), 8); diff --git a/companion/src/firmwares/customfunctiondata.h b/companion/src/firmwares/customfunctiondata.h index 93f9969e8..9e597441f 100644 --- a/companion/src/firmwares/customfunctiondata.h +++ b/companion/src/firmwares/customfunctiondata.h @@ -35,7 +35,8 @@ class RadioDataConversionState; enum AssignFunc { FuncOverrideCH1 = 0, - FuncOverrideCH32 = FuncOverrideCH1+CPN_MAX_CHNOUT-1, + FuncOverrideCHLast = FuncOverrideCH1 + CPN_MAX_CHNOUT - 1, + FuncOverrideCH32 = FuncOverrideCHLast, // TODO remove FuncTrainer, FuncTrainerRUD, FuncTrainerELE, @@ -47,8 +48,9 @@ enum AssignFunc { FuncPlayHaptic, FuncReset, FuncSetTimer1, - FuncSetTimer2, - FuncSetTimer3, + FuncSetTimer2, // TODO remove + FuncSetTimer3, // TODO remove + FuncSetTimerLast = FuncSetTimer1 + CPN_MAX_TIMERS - 1, FuncVario, FuncPlayPrompt, FuncPlayBoth, @@ -61,7 +63,7 @@ enum AssignFunc { FuncBackgroundMusic, FuncBackgroundMusicPause, FuncAdjustGV1, - FuncAdjustGVLast = FuncAdjustGV1+CPN_MAX_GVARS-1, + FuncAdjustGVLast = FuncAdjustGV1 + CPN_MAX_GVARS - 1, FuncSetFailsafe, FuncRangeCheckInternalModule, FuncRangeCheckExternalModule, @@ -83,7 +85,15 @@ class CustomFunctionData { Q_DECLARE_TR_FUNCTIONS(CustomFunctionData) public: - CustomFunctionData(AssignFunc func=FuncOverrideCH1) { clear(); this->func = func; } + enum CustomFunctionContext + { + GlobalFunctionsContext = 0x01, + SpecialFunctionsContext = 0x02, + + AllFunctionContexts = GlobalFunctionsContext | SpecialFunctionsContext + }; + + CustomFunctionData(AssignFunc func = FuncOverrideCH1) { clear(); this->func = func; } RawSwitch swtch; AssignFunc func; int param; @@ -95,7 +105,7 @@ class CustomFunctionData { void clear(); bool isEmpty() const; QString nameToString(int index, bool globalContext = false) const; - QString funcToString(const ModelData * model = NULL) const; + QString funcToString(const ModelData * model = nullptr) const; QString paramToString(const ModelData * model) const; QString repeatToString() const; QString enabledToString() const; @@ -103,6 +113,10 @@ class CustomFunctionData { static void populateResetParams(const ModelData * model, QComboBox * b, unsigned int value); static void populatePlaySoundParams(QStringList & qs); static void populateHapticParams(QStringList & qs); + static bool isFuncAvailable(int index); + static int funcContext(int index); + static int resetParamCount(const ModelData * model); + static bool isResetParamAvailable(const ModelData * model, int index); void convert(RadioDataConversionState & cstate); diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 930df092c..7657e2798 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -1439,3 +1439,42 @@ void ModelData::updateResetParam(CustomFunctionData * cfd) updRefInfo.updcnt++; } } + +QString ModelData::thrTraceSrcToString() const +{ + return thrTraceSrcToString((int)thrTraceSrc); +} + +QString ModelData::thrTraceSrcToString(const int index) const +{ + Firmware * firmware = getCurrentFirmware(); + const Boards board = Boards(getCurrentBoard()); + const int pscnt = board.getCapability(Board::Pots) + board.getCapability(Board::Sliders); + + if (index == 0) + return tr("THR"); + else if (index <= pscnt) + return board.getAnalogInputName(index + board.getCapability(Board::Sticks) - 1); + else if (index <= pscnt + firmware->getCapability(Outputs)) + return RawSource(SOURCE_TYPE_CH, index - pscnt - 1).toString(this); + + return QString(CPN_STR_UNKNOWN_ITEM); +} + +int ModelData::thrTraceSrcCount() const +{ + const Boards board = Boards(getCurrentBoard()); + Firmware * firmware = getCurrentFirmware(); + + return 1 + board.getCapability(Board::Pots) + board.getCapability(Board::Sliders) + firmware->getCapability(Outputs); +} + +bool ModelData::isThrTraceSrcAvailable(const GeneralSettings * generalSettings, const int index) const +{ + const Boards board = Boards(getCurrentBoard()); + + if (index > 0 && index <= board.getCapability(Board::Pots) + board.getCapability(Board::Sliders)) + return RawSource(SOURCE_TYPE_STICK, index + board.getCapability(Board::Sticks) - 1).isAvailable(this, generalSettings, board.getBoardType()); + else + return true; +} diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index 7eacdc26d..2b277568f 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -283,6 +283,10 @@ class ModelData { bool hasExpoChildren(const int index); bool hasExpoSiblings(const int index); void removeMix(const int idx); + QString thrTraceSrcToString() const; + QString thrTraceSrcToString(const int index) const; + int thrTraceSrcCount() const; + bool isThrTraceSrcAvailable(const GeneralSettings * generalSettings, const int index) const; protected: void removeGlobalVar(int & var); diff --git a/companion/src/firmwares/rawsource.cpp b/companion/src/firmwares/rawsource.cpp index 67a640c33..67ad743b6 100644 --- a/companion/src/firmwares/rawsource.cpp +++ b/companion/src/firmwares/rawsource.cpp @@ -143,14 +143,14 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con static const QString rotary[] = { tr("REa"), tr("REb") }; if (index<0) { - return tr("???"); + return QString(CPN_STR_UNKNOWN_ITEM); } QString result; int genAryIdx = 0; switch (type) { case SOURCE_TYPE_NONE: - return tr("----"); + return QString(CPN_STR_NONE_ITEM); case SOURCE_TYPE_VIRTUAL_INPUT: { @@ -161,7 +161,7 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con } case SOURCE_TYPE_LUA_OUTPUT: - return tr("LUA%1%2").arg(index/16+1).arg(QChar('a'+index%16)); + return tr("LUA%1%2").arg(index / 16 + 1).arg(QChar('a' + index % 16)); case SOURCE_TYPE_STICK: if (generalSettings) { @@ -193,10 +193,10 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con return result; case SOURCE_TYPE_CUSTOM_SWITCH: - return RawSwitch(SWITCH_TYPE_VIRTUAL, index+1).toString(); + return RawSwitch(SWITCH_TYPE_VIRTUAL, index + 1).toString(); case SOURCE_TYPE_CYC: - return tr("CYC%1").arg(index+1); + return tr("CYC%1").arg(index + 1); case SOURCE_TYPE_PPM: return RadioData::getElementName(tr("TR", "as in Trainer"), index + 1); @@ -208,18 +208,15 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con return LimitData().nameToString(index); case SOURCE_TYPE_SPECIAL: - result = CHECK_IN_ARRAY(special, index); - // TODO refactor timers into own source type - if (result.startsWith("Timer")) { - bool ok; - int n = result.right(1).toInt(&ok); - if (ok) { - if (model) - result = model->timers[n - 1].nameToString(n - 1); - else - result = TimerData().nameToString(n - 1); - } + if (index >= SOURCE_TYPE_SPECIAL_TIMER1_IDX && index <= SOURCE_TYPE_SPECIAL_TIMER1_IDX + CPN_MAX_TIMERS - 1) { + if (model) + result = model->timers[index - SOURCE_TYPE_SPECIAL_TIMER1_IDX].nameToString(index - SOURCE_TYPE_SPECIAL_TIMER1_IDX); + else + result = TimerData().nameToString(index - SOURCE_TYPE_SPECIAL_TIMER1_IDX); } + else + result = CHECK_IN_ARRAY(special, index); + return result; case SOURCE_TYPE_TELEMETRY: @@ -241,7 +238,7 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con return GVarData().nameToString(index); default: - return tr("???"); + return QString(CPN_STR_UNKNOWN_ITEM); } } @@ -409,4 +406,3 @@ QStringList RawSource::getSwitchList(Boards board) const } return ret; } - diff --git a/companion/src/firmwares/rawsource.h b/companion/src/firmwares/rawsource.h index 9d0b19450..a7d293173 100644 --- a/companion/src/firmwares/rawsource.h +++ b/companion/src/firmwares/rawsource.h @@ -178,6 +178,9 @@ enum RawSourceType { MAX_SOURCE_TYPE }; +constexpr int SOURCE_TYPE_STICK_THR_IDX { 3 }; // TODO is there a function to determine index? +constexpr int SOURCE_TYPE_SPECIAL_TIMER1_IDX { 2 }; // TODO temp const until Timers own source type + class RawSourceRange { Q_DECLARE_TR_FUNCTIONS(RawSourceRange) diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index a9b8d7640..2b01f2600 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -22,6 +22,7 @@ #include "radiodata.h" #include "modeldata.h" +#include "eeprominterface.h" void SensorData::updateUnit() { @@ -118,3 +119,40 @@ bool SensorData::isEmpty() const { return (!isAvailable() && type == 0 && id == 0 && subid == 0 && instance == 0 && rxIdx == 0 && moduleIdx == 0 && unit == 0 && ratio == 0 && prec == 0 && offset == 0 && autoOffset == 0 && filter == 0 && onlyPositive == 0 && logs == 0); } + +// static +QString SensorData::sourceToString(const ModelData * model, const int index, const bool sign) +{ + if (model) { + const QString prfx = sign ? index < 0 ? "-" : "" : ""; + + if (abs(index) > 0) { + const SensorData &sd = model->sensorData[abs(index) - 1]; + if (sd.type == SensorData::TELEM_TYPE_CUSTOM) + return QString("%1%2 (%3)").arg(prfx).arg(sd.label).arg(sd.getOrigin(model)); + else + return QString("%1%2").arg(prfx).arg(sd.label); + } + else + return CPN_STR_NONE_ITEM; + } + else + return ""; +} + +// static +bool SensorData::isSourceAvailable(const ModelData * model, const int index) +{ + if (model) { + Firmware * firmware = getCurrentFirmware(); + const int i = abs(index); + if (i <= firmware->getCapability(Sensors)) { + if (i > 0) + return model->sensorData[i - 1].isAvailable(); + else + return true; + } + } + + return false; +} diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index a2b993bcc..200f6e889 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -168,6 +168,9 @@ class SensorData { QString getOrigin(const ModelData* model) const; void clear() { memset(this, 0, sizeof(SensorData)); } bool isEmpty() const; + + static QString sourceToString(const ModelData * model, const int index, const bool sign = true); + static bool isSourceAvailable(const ModelData * model, const int index); }; #endif // SENSORDATA_H From bab16c912bf643ca40492c9fb4bfcc52b18b1f11 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 20 Dec 2020 13:54:12 +0100 Subject: [PATCH 05/65] Fix DMA SBUS trainer on colorlcd --- .../targets/common/arm/stm32/aux_serial_driver.cpp | 11 +---------- radio/src/targets/horus/board.h | 1 + radio/src/targets/horus/trainer_driver.cpp | 11 +++++++++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp index 5c8f1e97f..b2c528dc0 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -213,10 +213,6 @@ extern "C" void AUX_SERIAL_USART_IRQHandler(void) #if defined(LUA) & !defined(CLI) if (luaRxFifo && auxSerialMode == UART_MODE_LUA) luaRxFifo->push(data); -#endif -#if !defined(BOOT) - if (auxSerialMode == UART_MODE_SBUS_TRAINER) - trainerSbusFifo.push(data); #endif } status = AUX_SERIAL_USART->SR; @@ -328,7 +324,7 @@ void aux2SerialInit(unsigned int mode, unsigned int protocol) break; case UART_MODE_SBUS_TRAINER: - aux2SerialSetup(SBUS_BAUDRATE, false, USART_WordLength_9b, USART_Parity_Even, USART_StopBits_2); // 2 stop bits requires USART_WordLength_9b + aux2SerialSetup(SBUS_BAUDRATE, true, USART_WordLength_9b, USART_Parity_Even, USART_StopBits_2); // 2 stop bits requires USART_WordLength_9b AUX2_SERIAL_POWER_ON(); break; @@ -415,11 +411,6 @@ extern "C" void AUX2_SERIAL_USART_IRQHandler(void) if (luaRxFifo && aux2SerialMode == UART_MODE_LUA) { luaRxFifo->push(data); } -#endif -#if !defined(BOOT) - if (aux2SerialMode == UART_MODE_SBUS_TRAINER) { - trainerSbusFifo.push(data); - } #endif } status = AUX2_SERIAL_USART->SR; diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index 38daec33c..cffc553dc 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -713,6 +713,7 @@ void bluetoothDisable(); extern DMAFifo<512> telemetryFifo; typedef DMAFifo<32> AuxSerialRxFifo; extern AuxSerialRxFifo auxSerialRxFifo; +extern AuxSerialRxFifo aux2SerialRxFifo; extern volatile uint32_t externalModulePort; #endif diff --git a/radio/src/targets/horus/trainer_driver.cpp b/radio/src/targets/horus/trainer_driver.cpp index a4f0c7b6b..ce649c688 100644 --- a/radio/src/targets/horus/trainer_driver.cpp +++ b/radio/src/targets/horus/trainer_driver.cpp @@ -160,9 +160,16 @@ extern "C" void TRAINER_TIMER_IRQHandler() int sbusGetByte(uint8_t * byte) { switch (currentTrainerMode) { -#if defined(AUX_SERIAL) +#if defined(AUX_SERIAL) || defined(AUX2_SERIAL) case TRAINER_MODE_MASTER_BATTERY_COMPARTMENT: - return trainerSbusFifo.pop(*byte); +#if defined(AUX_SERIAL) + if (auxSerialMode == UART_MODE_SBUS_TRAINER) + return auxSerialRxFifo.pop(*byte); +#endif +#if defined(AUX2_SERIAL) + if (aux2SerialMode == UART_MODE_SBUS_TRAINER) + return aux2SerialRxFifo.pop(*byte); +#endif #endif default: return false; From 613ed785271c8b0e5cc1ef902bbb578bf441a20a Mon Sep 17 00:00:00 2001 From: elecpower Date: Mon, 21 Dec 2020 23:29:30 +1100 Subject: [PATCH 06/65] Curve reference add ui data models and fix invalid memory crashes --- .../src/datamodels/rawitemdatamodels.cpp | 24 +++ companion/src/datamodels/rawitemdatamodels.h | 36 ++++- companion/src/firmwares/curvereference.cpp | 137 +++++++++--------- companion/src/firmwares/curvereference.h | 27 ++-- companion/src/modeledit/expodialog.cpp | 5 +- companion/src/modeledit/expodialog.h | 2 +- companion/src/modeledit/inputs.cpp | 10 +- companion/src/modeledit/inputs.h | 1 + companion/src/modeledit/mixerdialog.cpp | 6 +- companion/src/modeledit/mixerdialog.h | 3 +- companion/src/modeledit/mixes.cpp | 10 +- companion/src/modeledit/mixes.h | 1 + 12 files changed, 166 insertions(+), 96 deletions(-) diff --git a/companion/src/datamodels/rawitemdatamodels.cpp b/companion/src/datamodels/rawitemdatamodels.cpp index 3805b98d6..c421ff964 100644 --- a/companion/src/datamodels/rawitemdatamodels.cpp +++ b/companion/src/datamodels/rawitemdatamodels.cpp @@ -505,3 +505,27 @@ void dumpModelContents(QString desc, AbstractRawItemDataModel * dataModel) else qDebug() << "model:" << desc << "did not cast"; } + +CurveRefTypeItemModel::CurveRefTypeItemModel(QObject * parent) : + AbstractStandardItemModel(parent) +{ + for (int i = 0; i <= CurveReference::MAX_CURVE_REF_TYPE; i++) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setText(CurveReference::typeToString((CurveReference::CurveRefType)i)); + modelItem->setData(i, ItemIdRole); + modelItem->setData(CurveReference::isTypeAvailable((CurveReference::CurveRefType)i), IsAvailableRole); + appendRow(modelItem); + } +} + +CurveRefFuncItemModel::CurveRefFuncItemModel(QObject * parent) : + AbstractStandardItemModel(parent) +{ + for (int i = 1; i <= CurveReference::functionCount(); i++) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setText(CurveReference::functionToString(i)); + modelItem->setData(i, ItemIdRole); + modelItem->setData(CurveReference::isFunctionAvailable(i), IsAvailableRole); + appendRow(modelItem); + } +} diff --git a/companion/src/datamodels/rawitemdatamodels.h b/companion/src/datamodels/rawitemdatamodels.h index fbcfe8be9..8c8a78cc4 100644 --- a/companion/src/datamodels/rawitemdatamodels.h +++ b/companion/src/datamodels/rawitemdatamodels.h @@ -30,11 +30,20 @@ class GeneralSettings; class ModelData; class AdjustmentReference; -class AbstractRawItemDataModel: public QStandardItemModel +class AbstractStandardItemModel: public QStandardItemModel { Q_OBJECT public: - enum DataRoles { ItemIdRole = Qt::UserRole, ItemTypeRole, ItemFlagsRole, IsAvailableRole }; + explicit AbstractStandardItemModel(QObject * parent = nullptr) : + QStandardItemModel(parent) + {} + + enum DataRoles { + ItemIdRole = Qt::UserRole, + ItemTypeRole, + ItemFlagsRole, + IsAvailableRole + }; Q_ENUM(DataRoles) enum DataGroups { @@ -43,9 +52,14 @@ class AbstractRawItemDataModel: public QStandardItemModel PositiveGroup = 0x04 }; Q_ENUM(DataGroups) +}; +class AbstractRawItemDataModel: public AbstractStandardItemModel +{ + Q_OBJECT + public: explicit AbstractRawItemDataModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr) : - QStandardItemModel(parent), + AbstractStandardItemModel(parent), generalSettings(generalSettings), modelData(modelData) {} @@ -221,4 +235,20 @@ class CommonItemModels: public QObject void dumpModelContents(QString desc, AbstractRawItemDataModel * dataModel); + +class CurveRefTypeItemModel : public AbstractStandardItemModel +{ + Q_OBJECT + public: + explicit CurveRefTypeItemModel(QObject * parent = nullptr); +}; + +class CurveRefFuncItemModel : public AbstractStandardItemModel +{ + Q_OBJECT + public: + explicit CurveRefFuncItemModel(QObject * parent = nullptr); +}; + + #endif // RAWITEMDATAMODELS_H diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 6ee2e38c5..688b4188f 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -91,8 +91,6 @@ QString CurveReference::typeToString(const CurveRefType type) return strl.at(idx); } -constexpr int MAX_CURVE_REF_FUNC { 6 }; - // static QString CurveReference::functionToString(const int value) { @@ -105,25 +103,50 @@ QString CurveReference::functionToString(const int value) return strl.at(idx); } -/* +// static +bool CurveReference::isTypeAvailable(const CurveRefType type) +{ + bool ret = false; + Firmware * fw = getCurrentFirmware(); + switch(type) { + case CURVE_REF_DIFF: + if (fw->getCapability(HasInputDiff)) + ret = true; + break; + case CURVE_REF_EXPO: + if (fw->getCapability(HasMixerExpo)) + ret = true; + break; + case CURVE_REF_FUNC: + case CURVE_REF_CUSTOM: + ret = true; + break; + } + + return ret; +} + +// static +bool CurveReference::isFunctionAvailable(const int value) +{ + return true; +} + +// static +int CurveReference::functionCount() +{ + return 6; +} + + +/* * CurveReferenceUIManager */ -constexpr int CURVE_REF_UI_HIDE_DIFF { 0x01 }; -constexpr int CURVE_REF_UI_HIDE_EXPO { 0x02 }; -constexpr int CURVE_REF_UI_HIDE_NEGATIVE_CURVES { 0x04 }; - -// static -bool CurveReferenceUIManager::firsttime { true }; -int CurveReferenceUIManager::flags { 0 }; -bool CurveReferenceUIManager::hasCapabilityGvars { false }; -int CurveReferenceUIManager::numCurves { 0 }; -RawItemFilteredModel * CurveReferenceUIManager::curveItemModel { nullptr }; -QStandardItemModel * CurveReferenceUIManager::tempModel { nullptr }; - CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveValueCB, CurveReference & curve, const ModelData & model, - RawItemFilteredModel * curveItemModel, QObject * parent) : + RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel, + QObject * parent) : QObject(parent), curveTypeCB(nullptr), curveGVarCB(nullptr), @@ -131,14 +154,17 @@ CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveValueCB, Curve curveValueCB(curveValueCB), curve(curve), model(model), - lock(false) + lock(false), + curveItemModel(curveItemModel), + gvarItemModel(gvarItemModel) { - init(curveItemModel); + init(parent); } CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheckBox * curveGVarCB, QSpinBox * curveValueSB, QComboBox * curveValueCB, CurveReference & curve, const ModelData & model, - RawItemFilteredModel * curveItemModel, QObject * parent) : + RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel, + QObject * parent) : QObject(parent), curveTypeCB(curveTypeCB), curveGVarCB(curveGVarCB), @@ -146,36 +172,25 @@ CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheck curveValueCB(curveValueCB), curve(curve), model(model), - lock(false) + lock(false), + curveItemModel(curveItemModel), + gvarItemModel(gvarItemModel) { - init(curveItemModel); + init(parent); } CurveReferenceUIManager::~CurveReferenceUIManager() { - delete tempModel; } -void CurveReferenceUIManager::init(RawItemFilteredModel * curveModel) +void CurveReferenceUIManager::init(QObject * parent) { - tempModel = new QStandardItemModel(); - - if (firsttime) { - firsttime = false; - Firmware * fw = getCurrentFirmware(); - hasCapabilityGvars = fw->getCapability(Gvars); - numCurves = fw->getCapability(NumCurves); - curveItemModel = curveModel; - - if (!fw->getCapability(HasInputDiff)) - flags |= (CURVE_REF_UI_HIDE_DIFF | CURVE_REF_UI_HIDE_NEGATIVE_CURVES); - - if (!fw->getCapability(HasMixerExpo)) - flags |= CURVE_REF_UI_HIDE_EXPO; - } + hasCapabilityGvars = getCurrentFirmware()->getCapability(Gvars); if (curveTypeCB) { - populateTypeCB(curveTypeCB, curve); + typeItemModel = new RawItemFilteredModel(new CurveRefTypeItemModel(parent), parent); + curveTypeCB->setModel(typeItemModel); + curveTypeCB->setCurrentIndex(curveTypeCB->findData((int)curve.type)); connect(curveTypeCB, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int))); } @@ -188,9 +203,12 @@ void CurveReferenceUIManager::init(RawItemFilteredModel * curveModel) connect(curveValueSB, SIGNAL(editingFinished()), this, SLOT(valueSBChanged())); } - curveValueCB->setSizeAdjustPolicy(QComboBox::AdjustToContents); - curveValueCB->setMaxVisibleItems(10); - connect(curveValueCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valueCBChanged())); + if (curveValueCB) { + funcItemModel = new RawItemFilteredModel(new CurveRefFuncItemModel(parent), parent); + curveValueCB->setSizeAdjustPolicy(QComboBox::AdjustToContents); + curveValueCB->setMaxVisibleItems(10); + connect(curveValueCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valueCBChanged())); + } update(); } @@ -231,7 +249,7 @@ void CurveReferenceUIManager::update() curveValueSB->setVisible(widgetsMask & CURVE_REF_UI_VALUE_SHOW); if(curveValueCB) { if (curve.isValueReference()) - populateValueCB(curveValueCB, curve, &model); + populateValueCB(curveValueCB); curveValueCB->setVisible(widgetsMask & CURVE_REF_UI_REF_SHOW); } @@ -271,39 +289,16 @@ void CurveReferenceUIManager::valueCBChanged() } } -// static -void CurveReferenceUIManager::populateTypeCB(QComboBox * cb, const CurveReference & curveRef) +void CurveReferenceUIManager::populateValueCB(QComboBox * cb) { if (cb) { - cb->clear(); - for (int i = 0; i <= CurveReference::MAX_CURVE_REF_TYPE; i++) { - if ((curveRef.type == CurveReference::CURVE_REF_DIFF && !(flags & CURVE_REF_UI_HIDE_DIFF)) || - (curveRef.type == CurveReference::CURVE_REF_EXPO && !(flags & CURVE_REF_UI_HIDE_EXPO)) || - (curveRef.type != CurveReference::CURVE_REF_DIFF && curveRef.type != CurveReference::CURVE_REF_EXPO)) - cb->addItem(CurveReference::typeToString((CurveReference::CurveRefType)i), i); - } - - cb->setCurrentIndex(cb->findData((int)curveRef.type)); - } -} - -// static -void CurveReferenceUIManager::populateValueCB(QComboBox * cb, const CurveReference & curveRef, const ModelData * model) -{ - if (cb) { - cb->setModel(tempModel); // do not want to clear/alter the shared curves model and set to nullptr is invalid - - switch (curveRef.type) { + switch (curve.type) { case CurveReference::CURVE_REF_DIFF: case CurveReference::CURVE_REF_EXPO: - cb->clear(); - Helpers::populateGVCB(*cb, curveRef.value, *model); + cb->setModel(gvarItemModel); break; case CurveReference::CURVE_REF_FUNC: - cb->clear(); - for (int i = 1; i <= MAX_CURVE_REF_FUNC; i++) { - cb->addItem(CurveReference::functionToString(i), i); - } + cb->setModel(funcItemModel); break; case CurveReference::CURVE_REF_CUSTOM: cb->setModel(curveItemModel); @@ -312,6 +307,6 @@ void CurveReferenceUIManager::populateValueCB(QComboBox * cb, const CurveReferen break; } - cb->setCurrentIndex(cb->findData(curveRef.value)); + cb->setCurrentIndex(cb->findData(curve.value)); } } diff --git a/companion/src/firmwares/curvereference.h b/companion/src/firmwares/curvereference.h index 018fb12aa..119aaf597 100644 --- a/companion/src/firmwares/curvereference.h +++ b/companion/src/firmwares/curvereference.h @@ -79,6 +79,9 @@ class CurveReference { static int getDefaultValue(const CurveRefType type, const bool isGVar = false); static QString typeToString(const CurveRefType type); static QString functionToString(const int value); + static bool isTypeAvailable(const CurveRefType type); + static bool isFunctionAvailable(const int value); + static int functionCount(); }; class CurveReferenceUIManager : public QObject { @@ -87,18 +90,18 @@ class CurveReferenceUIManager : public QObject { public: CurveReferenceUIManager(QComboBox *curveValueCB, CurveReference & curve, const ModelData & model, - RawItemFilteredModel * curveItemModel, QObject * parent = nullptr); + RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel, QObject * parent = nullptr); CurveReferenceUIManager(QComboBox *curveTypeCB, QCheckBox *curveGVarCB, QSpinBox *curveValueSB, QComboBox *curveValueCB, - CurveReference & curve, const ModelData & model, RawItemFilteredModel * curveItemModel, QObject * parent = nullptr); + CurveReference & curve, const ModelData & model, RawItemFilteredModel * curveItemModel, + RawItemFilteredModel * gvarItemModel, QObject * parent = nullptr); virtual ~CurveReferenceUIManager(); - void init(RawItemFilteredModel * curveModel); - void update(); protected slots: void gvarCBChanged(int); void typeChanged(int); void valueSBChanged(); void valueCBChanged(); + void update(); protected: QComboBox *curveTypeCB; @@ -108,16 +111,16 @@ class CurveReferenceUIManager : public QObject { CurveReference & curve; const ModelData & model; bool lock; + bool hasCapabilityGvars; + RawItemFilteredModel * curveItemModel; + RawItemFilteredModel * gvarItemModel; + RawItemFilteredModel * typeItemModel; + RawItemFilteredModel * funcItemModel; - static bool firsttime; - static int flags; - static bool hasCapabilityGvars; - static int numCurves; - static RawItemFilteredModel * curveItemModel; - static QStandardItemModel * tempModel; + void populateValueCB(QComboBox * cb); - static void populateTypeCB(QComboBox * cb, const CurveReference & curveRef); - static void populateValueCB(QComboBox * cb, const CurveReference & curveRef, const ModelData * model = nullptr); + private: + void init(QObject * parent); }; #endif // CURVEREFERENCE_H diff --git a/companion/src/modeledit/expodialog.cpp b/companion/src/modeledit/expodialog.cpp index afebbeb27..217f787d2 100644 --- a/companion/src/modeledit/expodialog.cpp +++ b/companion/src/modeledit/expodialog.cpp @@ -25,7 +25,7 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, GeneralSettings & generalSettings, Firmware * firmware, QString & inputName, RawItemFilteredModel * rawSourceModel, - RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel) : + RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel) : QDialog(parent), ui(new Ui::ExpoDialog), model(model), @@ -50,7 +50,8 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, -100, 100); gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, ed->offset, model, 0, -100, 100); - curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, curveItemModel, this); + curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, curveItemModel, + gvarItemModel, this); ui->switchesCB->setModel(rawSwitchModel); ui->switchesCB->setCurrentIndex(ui->switchesCB->findData(ed->swtch.toValue())); diff --git a/companion/src/modeledit/expodialog.h b/companion/src/modeledit/expodialog.h index b77d55852..0b17900df 100644 --- a/companion/src/modeledit/expodialog.h +++ b/companion/src/modeledit/expodialog.h @@ -38,7 +38,7 @@ class ExpoDialog : public QDialog { public: ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expodata, GeneralSettings & generalSettings, Firmware * firmware, QString & inputName, RawItemFilteredModel * rawSourceItemModel, - RawItemFilteredModel * rawSwitchItemModel, RawItemFilteredModel * curveItemModel); + RawItemFilteredModel * rawSwitchItemModel, RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel); ~ExpoDialog(); protected: diff --git a/companion/src/modeledit/inputs.cpp b/companion/src/modeledit/inputs.cpp index ee2347aee..5e50de71c 100644 --- a/companion/src/modeledit/inputs.cpp +++ b/companion/src/modeledit/inputs.cpp @@ -37,10 +37,15 @@ InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & g connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); - curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), RawItemFilteredModel::AllFilter, this); + const int filter = firmware->getCapability(HasInputDiff) ? RawItemFilteredModel::AllFilter : RawItemFilteredModel::PositiveFilter; + curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), filter, this); connect(curveFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); connect(curveFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); + gvarFilteredModel = new RawItemFilteredModel(commonItemModels->gvarItemModel(), RawItemFilteredModel::AllFilter, this); + connect(gvarFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); + connect(gvarFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); + inputsCount = firmware->getCapability(VirtualInputs); if (inputsCount == 0) inputsCount = CPN_MAX_STICKS; @@ -194,7 +199,8 @@ void InputsPanel::gm_openExpo(int index) if (firmware->getCapability(VirtualInputs)) inputName = model->inputNames[ed.chn]; - ExpoDialog *dlg = new ExpoDialog(this, *model, &ed, generalSettings, firmware, inputName, rawSourceFilteredModel, rawSwitchFilteredModel, curveFilteredModel); + ExpoDialog *dlg = new ExpoDialog(this, *model, &ed, generalSettings, firmware, inputName, rawSourceFilteredModel, rawSwitchFilteredModel, + curveFilteredModel, gvarFilteredModel); if (dlg->exec()) { model->expoData[index] = ed; if (firmware->getCapability(VirtualInputs)) diff --git a/companion/src/modeledit/inputs.h b/companion/src/modeledit/inputs.h index ea1243213..3bbfceb7b 100644 --- a/companion/src/modeledit/inputs.h +++ b/companion/src/modeledit/inputs.h @@ -76,6 +76,7 @@ class InputsPanel : public ModelPanel RawItemFilteredModel *rawSourceFilteredModel; RawItemFilteredModel *rawSwitchFilteredModel; RawItemFilteredModel *curveFilteredModel; + RawItemFilteredModel *gvarFilteredModel; int getExpoIndex(unsigned int dch); bool gm_insertExpo(int idx); diff --git a/companion/src/modeledit/mixerdialog.cpp b/companion/src/modeledit/mixerdialog.cpp index 00d2badc8..102d2d2a9 100644 --- a/companion/src/modeledit/mixerdialog.cpp +++ b/companion/src/modeledit/mixerdialog.cpp @@ -25,7 +25,8 @@ #include "helpers.h" MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, GeneralSettings & generalSettings, Firmware * firmware, - RawItemFilteredModel * rawSourceModel, RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel) : + RawItemFilteredModel * rawSourceModel, RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel, + RawItemFilteredModel * gvarItemModel) : QDialog(parent), ui(new Ui::MixerDialog), model(model), @@ -52,7 +53,8 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, md->weight, model, 100, -limit, limit); gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, md->sOffset, model, 0, -limit, limit); - curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, md->curve, model, curveItemModel, this); + curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, md->curve, model, curveItemModel, + gvarItemModel, this); ui->MixDR_CB->setChecked(md->noExpo == 0); diff --git a/companion/src/modeledit/mixerdialog.h b/companion/src/modeledit/mixerdialog.h index 6d2131de2..d036832d9 100644 --- a/companion/src/modeledit/mixerdialog.h +++ b/companion/src/modeledit/mixerdialog.h @@ -36,7 +36,8 @@ class MixerDialog : public QDialog { Q_OBJECT public: MixerDialog(QWidget *parent, ModelData & model, MixData *mixdata, GeneralSettings & generalSettings, Firmware * firmware, - RawItemFilteredModel * rawSourceModel, RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel); + RawItemFilteredModel * rawSourceModel, RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel, + RawItemFilteredModel * gvarItemModel); ~MixerDialog(); protected: diff --git a/companion/src/modeledit/mixes.cpp b/companion/src/modeledit/mixes.cpp index f02a57417..01b4d7f64 100644 --- a/companion/src/modeledit/mixes.cpp +++ b/companion/src/modeledit/mixes.cpp @@ -37,10 +37,15 @@ MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & gen connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); - curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), RawItemFilteredModel::AllFilter, this); + const int filter = firmware->getCapability(HasMixerExpo) ? RawItemFilteredModel::AllFilter : RawItemFilteredModel::PositiveFilter; + curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), filter, this); connect(curveFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); connect(curveFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); + gvarFilteredModel = new RawItemFilteredModel(commonItemModels->gvarItemModel(), RawItemFilteredModel::AllFilter, this); + connect(gvarFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); + connect(gvarFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); + QGridLayout * mixesLayout = new QGridLayout(this); mixersListWidget = new MixersListWidget(this, false); // TODO enum @@ -189,7 +194,8 @@ void MixesPanel::gm_openMix(int index) MixData mixd(model->mixData[index]); - MixerDialog *dlg = new MixerDialog(this, *model, &mixd, generalSettings, firmware, rawSourceFilteredModel, rawSwitchFilteredModel, curveFilteredModel); + MixerDialog *dlg = new MixerDialog(this, *model, &mixd, generalSettings, firmware, rawSourceFilteredModel, rawSwitchFilteredModel, + curveFilteredModel, gvarFilteredModel); if(dlg->exec()) { model->mixData[index] = mixd; emit modified(); diff --git a/companion/src/modeledit/mixes.h b/companion/src/modeledit/mixes.h index 014786b74..154eda9b4 100644 --- a/companion/src/modeledit/mixes.h +++ b/companion/src/modeledit/mixes.h @@ -72,6 +72,7 @@ class MixesPanel : public ModelPanel RawItemFilteredModel *rawSourceFilteredModel; RawItemFilteredModel *rawSwitchFilteredModel; RawItemFilteredModel *curveFilteredModel; + RawItemFilteredModel *gvarFilteredModel; int getMixerIndex(unsigned int dch); bool gm_insertMix(int idx); From a141500b6a46b4e7dfa1c7d66074ee4cba25e914 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 21 Dec 2020 14:38:26 +0100 Subject: [PATCH 07/65] Add logs indicator display to topbar --- radio/src/gui/480x272/topbar.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/gui/480x272/topbar.cpp b/radio/src/gui/480x272/topbar.cpp index f78174aa8..e8027d047 100644 --- a/radio/src/gui/480x272/topbar.cpp +++ b/radio/src/gui/480x272/topbar.cpp @@ -61,6 +61,11 @@ void drawTopBar() lcdDrawBitmapPattern(LCD_W-98, 8, LBM_TOPMENU_USB, MENU_TITLE_COLOR); } + // Logs + if (isFunctionActive(FUNCTION_LOGS) && !usbPlugged() && BLINK_ON_PHASE) { + lcdDrawBitmapPattern(LCD_W-98, 6, LBM_DOT, MENU_TITLE_COLOR); + } + // RSSI const uint8_t rssiBarsValue[] = {30, 40, 50, 60, 80}; const uint8_t rssiBarsHeight[] = {5, 10, 15, 21, 31}; From 2e8e5d9c55f28a99946233ba6cccd6d5a39a7c42 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Dec 2020 08:36:23 +0100 Subject: [PATCH 08/65] Undo popup size change --- radio/src/gui/128x64/popups.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/128x64/popups.h b/radio/src/gui/128x64/popups.h index 9400d69f7..e5d483e0b 100644 --- a/radio/src/gui/128x64/popups.h +++ b/radio/src/gui/128x64/popups.h @@ -23,16 +23,16 @@ #include -#define MESSAGEBOX_X 1 +#define MESSAGEBOX_X 10 #define MESSAGEBOX_Y 16 -#define MESSAGEBOX_W (LCD_W - 2) +#define MESSAGEBOX_W (LCD_W - 19) #define MENU_X MESSAGEBOX_X #define MENU_Y MESSAGEBOX_Y #define MENU_W MESSAGEBOX_W #define WARNING_LINE_LEN 20 -#define WARNING_LINE_X 3 +#define WARNING_LINE_X 16 #define WARNING_LINE_Y 3*FH #define POPUP_MENU_MAX_LINES 12 From fa8f52281cdfd05a6acd36f1344b4c0c45f91df5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Dec 2020 08:37:14 +0100 Subject: [PATCH 09/65] Improve script errors output for small lcds --- radio/src/lua/interface.cpp | 22 +++++++++++++++++++--- radio/src/translations/cz.h.txt | 2 +- radio/src/translations/de.h.txt | 2 +- radio/src/translations/en.h.txt | 2 +- radio/src/translations/es.h.txt | 2 +- radio/src/translations/fi.h.txt | 2 +- radio/src/translations/fr.h.txt | 2 +- radio/src/translations/it.h.txt | 2 +- radio/src/translations/nl.h.txt | 2 +- radio/src/translations/pl.h.txt | 2 +- radio/src/translations/pt.h.txt | 2 +- radio/src/translations/se.h.txt | 2 +- 12 files changed, 30 insertions(+), 14 deletions(-) diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index a0cef2c1d..81ac4472a 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -745,11 +745,23 @@ void displayLuaError(const char * title) if (lua_warning_info[0]) { char * split = strstr(lua_warning_info, ": "); if (split) { - lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y+FH+3, lua_warning_info, split-lua_warning_info, SMLSIZE); - lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y+2*FH+2, split+2, lua_warning_info+LUA_WARNING_INFO_LEN-split, SMLSIZE); +#if LCD_W == 128 + if (strlen(split + 2) <= 20) { + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + FH + 3, lua_warning_info, split - lua_warning_info, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + 2 * FH + 2, split + 2, strlen(split + 2), SMLSIZE); + } + else { + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + FH, lua_warning_info, split - lua_warning_info, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + 2 * FH, split + 2, 20, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + 3 * FH, split + 22, strlen(split + 22), SMLSIZE); + } +#else + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + FH + 3, lua_warning_info, split - lua_warning_info, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + 2 * FH + 2, split + 2, lua_warning_info + LUA_WARNING_INFO_LEN - split, SMLSIZE); +#endif } else { - lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y+FH+3, lua_warning_info, 40, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + FH + 3, lua_warning_info, 40, SMLSIZE); } } } @@ -787,7 +799,11 @@ void luaError(lua_State * L, uint8_t error, bool acknowledge) #if defined(SIMU) if (!strncmp(msg, ".", 2)) msg += 1; #endif +#if LCD_W == 128 + msg = strrchr(msg, '/') + 1; +#else if (!strncmp(msg, "/SCRIPTS/", 9)) msg += 9; +#endif strncpy(lua_warning_info, msg, LUA_WARNING_INFO_LEN); lua_warning_info[LUA_WARNING_INFO_LEN] = '\0'; } diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 2f42f7edd..eed7492d1 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -780,7 +780,7 @@ #define TR_NO_MODELS_ON_SD "žádný model" BREAKSPACE "na SD" #define TR_NO_BITMAPS_ON_SD "žádné obrázky" BREAKSPACE "na SD" #define TR_NO_SCRIPTS_ON_SD "žádný skript" BREAKSPACE "na SD" -#define TR_SCRIPT_SYNTAX_ERROR "Syntaktická chyba skriptu" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntaktická chyba", "Syntaktická chyba skriptu") #define TR_SCRIPT_PANIC "Script zmaten" #define TR_SCRIPT_KILLED "Script ukončen" #define TR_SCRIPT_ERROR "Neznámá chyba" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 1e85008b0..d927fadda 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -782,7 +782,7 @@ #define TR_NO_MODELS_ON_SD "Kein Modelle" BREAKSPACE "auf SD" #define TR_NO_BITMAPS_ON_SD "Keine Bitmaps" BREAKSPACE "auf SD" #define TR_NO_SCRIPTS_ON_SD "Keine Skripte" BREAKSPACE "auf SD" -#define TR_SCRIPT_SYNTAX_ERROR "Skript Syntaxfehler" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntaxfehler", "Skript Syntaxfehler") #define TR_SCRIPT_PANIC "Skript Panik" #define TR_SCRIPT_KILLED "Skript beendet" #define TR_SCRIPT_ERROR "Unbekannter Fehler" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index ad49d6997..171dce2c4 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -786,7 +786,7 @@ #define TR_NO_MODELS_ON_SD "No models" BREAKSPACE "on SD" #define TR_NO_BITMAPS_ON_SD "No bitmaps" BREAKSPACE "on SD" #define TR_NO_SCRIPTS_ON_SD "No scripts" BREAKSPACE "on SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Script syntax error") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script killed" #define TR_SCRIPT_ERROR "Unknown error" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 72eb11dbc..279b4e90d 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -783,7 +783,7 @@ #define TR_NO_MODELS_ON_SD "Sin modelos en SD" #define TR_NO_BITMAPS_ON_SD "Sin imágenes en SD" #define TR_NO_SCRIPTS_ON_SD "No scripts en SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Script syntax error") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script killed" #define TR_SCRIPT_ERROR "Error desconocido" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index e18b51102..23bdc897c 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -799,7 +799,7 @@ #define TR_NO_MODELS_ON_SD "No Models on SD" #define TR_NO_BITMAPS_ON_SD "No Bitmaps on SD" #define TR_NO_SCRIPTS_ON_SD "No Scripts on SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Script syntax error") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script killed" #define TR_SCRIPT_ERROR "Unknown error" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index ffaeb38c6..0cf289578 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -802,7 +802,7 @@ #define TR_NO_MODELS_ON_SD "Aucun modèle SD" #define TR_NO_BITMAPS_ON_SD "Aucun Bitmap SD" #define TR_NO_SCRIPTS_ON_SD "Aucun Script SD" -#define TR_SCRIPT_SYNTAX_ERROR "Erreur syntaxe script" +#define TR_SCRIPT_SYNTAX_ERROR TR("Erreur syntaxe", "Erreur syntaxe script") #define TR_SCRIPT_PANIC "Script bloqué" #define TR_SCRIPT_KILLED "Script interrompu" #define TR_SCRIPT_ERROR "Erreur inconnue" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index c1e8c0be0..dc49ecef3 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -801,7 +801,7 @@ #define TR_NO_MODELS_ON_SD "No Model." BREAKSPACE "su SD" #define TR_NO_BITMAPS_ON_SD "No Immag." BREAKSPACE "su SD" #define TR_NO_SCRIPTS_ON_SD "No Scripts" BREAKSPACE "su SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script errore sintassi" +#define TR_SCRIPT_SYNTAX_ERROR TR("Errore sintassi", "Script errore sintassi") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script fermato" #define TR_SCRIPT_ERROR "Errore sconosciuto" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index fe5abbfe3..b39271755 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -787,7 +787,7 @@ #define TR_NO_MODELS_ON_SD "Geen Modellen" BREAKSPACE "op SD" #define TR_NO_BITMAPS_ON_SD "Geen Bitmaps" BREAKSPACE "op SD" #define TR_NO_SCRIPTS_ON_SD "Geen Scripts" BREAKSPACE "op SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Script syntax error") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script killed" #define TR_SCRIPT_ERROR "Unknown error" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 8212ce84d..4eb1a5139 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -800,7 +800,7 @@ #define TR_NO_MODELS_ON_SD "Brak modelu na SD" #define TR_NO_BITMAPS_ON_SD "Brak obrazków na SD" #define TR_NO_SCRIPTS_ON_SD "Brak skryptów na SD" -#define TR_SCRIPT_SYNTAX_ERROR "Skrypt:syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Skrypt:syntax error") #define TR_SCRIPT_PANIC "Skrypt:panic" #define TR_SCRIPT_KILLED "Skrypt:killed" #define TR_SCRIPT_ERROR "Nieznany błąd" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 412e3698a..8575e08bc 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -801,7 +801,7 @@ #define TR_NO_MODELS_ON_SD "Sem Modelo no SD" #define TR_NO_BITMAPS_ON_SD "No Bitmaps on SD" #define TR_NO_SCRIPTS_ON_SD "No Scripts on SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Script syntax error") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script killed" #define TR_SCRIPT_ERROR "Unknown error" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 57cf99ba6..c1877b88a 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -800,7 +800,7 @@ #define TR_NO_MODELS_ON_SD "Ingen modell i SD" #define TR_NO_BITMAPS_ON_SD "Ikoner saknas på SD" #define TR_NO_SCRIPTS_ON_SD "Programkod saknas på SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Script syntax error") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script killed" #define TR_SCRIPT_ERROR "Unknown error" From 0d281cf5dde3be01ae093a19b288cd78c218052d Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 31 Dec 2020 17:15:35 +1100 Subject: [PATCH 10/65] Major refactoring --- companion/src/datamodels/itemmodelfactory.cpp | 223 ++++++++ companion/src/datamodels/itemmodelfactory.h | 75 +++ .../src/datamodels/rawitemdatamodels.cpp | 509 ++++++++++-------- companion/src/datamodels/rawitemdatamodels.h | 268 +++++---- .../src/datamodels/rawitemfilteredmodel.cpp | 144 ++++- .../src/datamodels/rawitemfilteredmodel.h | 81 ++- companion/src/firmwares/curvereference.cpp | 50 +- companion/src/firmwares/curvereference.h | 28 +- companion/src/generaledit/generaledit.cpp | 9 +- companion/src/generaledit/generaledit.h | 4 +- companion/src/modeledit/channels.cpp | 15 +- companion/src/modeledit/channels.h | 16 +- companion/src/modeledit/curves.cpp | 7 +- companion/src/modeledit/curves.h | 6 +- companion/src/modeledit/customfunctions.cpp | 31 +- companion/src/modeledit/customfunctions.h | 21 +- companion/src/modeledit/expodialog.cpp | 13 +- companion/src/modeledit/expodialog.h | 9 +- companion/src/modeledit/flightmodes.cpp | 32 +- companion/src/modeledit/flightmodes.h | 19 +- companion/src/modeledit/heli.cpp | 14 +- companion/src/modeledit/heli.h | 15 +- companion/src/modeledit/inputs.cpp | 40 +- companion/src/modeledit/inputs.h | 22 +- companion/src/modeledit/logicalswitches.cpp | 22 +- companion/src/modeledit/logicalswitches.h | 16 +- companion/src/modeledit/mixerdialog.cpp | 13 +- companion/src/modeledit/mixerdialog.h | 6 +- companion/src/modeledit/mixes.cpp | 40 +- companion/src/modeledit/mixes.h | 22 +- companion/src/modeledit/modeledit.cpp | 34 +- companion/src/modeledit/modeledit.h | 10 +- companion/src/modeledit/setup.cpp | 31 +- companion/src/modeledit/setup.h | 20 +- companion/src/modeledit/telemetry.cpp | 329 ++++++----- companion/src/modeledit/telemetry.h | 54 +- companion/src/shared/autocombobox.h | 63 ++- 37 files changed, 1512 insertions(+), 799 deletions(-) create mode 100644 companion/src/datamodels/itemmodelfactory.cpp create mode 100644 companion/src/datamodels/itemmodelfactory.h diff --git a/companion/src/datamodels/itemmodelfactory.cpp b/companion/src/datamodels/itemmodelfactory.cpp new file mode 100644 index 000000000..892345568 --- /dev/null +++ b/companion/src/datamodels/itemmodelfactory.cpp @@ -0,0 +1,223 @@ +/* + * 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 "itemmodelfactory.h" + +#include "rawitemdatamodels.h" +#include "rawitemfilteredmodel.h" + +#include "eeprominterface.h" +#include "generalsettings.h" +#include "modeldata.h" + +ItemModelFactory::ItemModelFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData) : + generalSettings(generalSettings), + modelData(modelData) +{ + firmware = getCurrentFirmware(); + board = new Boards(getCurrentBoard()); + boardType = getCurrentBoard(); +} + +ItemModelFactory::~ItemModelFactory() +{ + unregisterItemModels(); + delete board; +} + +void ItemModelFactory::addItemModel(const AbstractItemModel::ItemModelId id) +{ + switch (id) { + case AbstractItemModel::RawSourceId: + registerItemModel(new RawSourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::RawSwitchId: + registerItemModel(new RawSwitchItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CurveId: + registerItemModel(new CurveItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::GVarRefId: + registerItemModel(new GVarReferenceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::ThrSourceId: + registerItemModel(new ThrottleSourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CustomFuncActionId: + registerItemModel(new CustomFuncActionItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CustomFuncResetParamId: + registerItemModel(new CustomFuncResetParamItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::TeleSourceId: + registerItemModel(new TelemetrySourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CurveRefTypeId: + registerItemModel(new CurveRefTypeItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CurveRefFuncId: + registerItemModel(new CurveRefFuncItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + default: + qDebug() << "Error: unknown item model: id"; + break; + } +} + +void ItemModelFactory::registerItemModel(QAbstractItemModel * itemModel) +{ + if (itemModel && !getItemModel(itemModel->getId())) { + registeredItemModels.push_back(itemModel); + } +} + +void ItemModelFactory::unregisterItemModels() +{ + foreach (QAbstractItemModel *itemModel, registeredItemModels) { + delete itemModel; + } +} + +void ItemModelFactory::unregisterItemModel(const AbstractItemModel::ItemModelId id) +{ + QAbstractItemModel * itemModel = getItemModel(id); + if (itemModel) + delete itemModel; +} + +QAbstractItemModel * ItemModelsFactory::getItemModel(const AbstractItemModel::ItemModelId id) const +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + if (itemModel->getId() == id) + return itemModel; + } + + return nullptr; +} + +void ItemModelFactory::update(const AbstractItemModel::UpdateTrigger trigger) +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + itemModel->update(trigger); + } +} + +void ItemModelFactory::dumpAllItemModelContents() const +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + AbstractItemModel::dumpItemModelContents(itemModel); + } +} + +// +// FilteredItemModelsFactory +// + +FilteredItemModelFactory::FilteredItemModelFactory() +{ +} + +FilteredItemModelFactory::~FilteredItemModelFactory() +{ + unregisterItemModels(); +} + +int FilteredItemModelFactory::registerItemModel(FilteredItemModel * itemModel, QString name) +{ + if (itemModel) { + if (isItemModelRegistered(name)) + qDebug() << "Warning: item model already registered with name:" << name; + const int id = registeredItemModels.count(); + itemModel->setId(id); + itemModel->setName(name); + registeredItemModels.push_back(itemModel); + return id; + } + return 0; +} + +void FilteredItemModelFactory::unregisterItemModels() +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + delete itemModel; + } +} + +void FilteredItemModelFactory::unregisterItemModel(const int id) +{ + FilteredItemModel * itemModel = getItemModel(id); + if (itemModel) + delete itemModel; +} + +bool FilteredItemModelFactory::isItemModelRegistered(const int id) const +{ + return getItemModel(id); +} + +bool FilteredItemModelFactory::isItemModelRegistered(const QString name) const +{ + return getItemModel(name); +} + +FilteredItemModel * FilteredItemModelFactory::getItemModel(const int id) const +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + if (itemModel->getId() == id) + return itemModel; + } + + return nullptr; +} + +FilteredItemModel * FilteredItemModelFactory::getItemModel(const QString name) const +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + if (itemModel->getName() == name) + return itemModel; + } + + return nullptr; +} + +void FilteredItemModelFactory::update() +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + itemModel->update(); + } +} + +void FilteredItemModelFactory::dumpItemModelContents(const int id) const +{ + FilteredItemModel::dumpItemModelContents(getItemModel(id)); +} + +void FilteredItemModelFactory::dumpItemModelContents(const QString name) const +{ + FilteredItemModel::dumpItemModelContents(getItemModel(name)); +} + +void FilteredItemModelFactory::dumpAllItemModelContents() const +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + FilteredItemModel::dumpItemModelContents(itemModel); + } +} + diff --git a/companion/src/datamodels/itemmodelfactory.h b/companion/src/datamodels/itemmodelfactory.h new file mode 100644 index 000000000..01b7626b4 --- /dev/null +++ b/companion/src/datamodels/itemmodelfactory.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#ifndef ITEMMODELFACTORY_H +#define ITEMMODELFACTORY_H + +class GeneralSettings; +class ModelData; +class AbstractItemModel; +class FilteredItemModel; + +class ItemModelFactory +{ + public: + ItemModelFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData); + virtual ~ItemModelFactory(); + + void addItemModel(const AbstractItemModel::ItemModelId id); + void registerItemModel(AbstractItemModel * itemModel); + void unregisterItemModels(); + void unregisterItemModel(const AbstractItemModel::ItemModelId id); + AbstractItemModel * getItemModel(const AbstractItemModel::ItemModelId id) const; + void update(const AbstractItemModel::UpdateTrigger trigger = AbstractItemModel::SystemRefresh); + void dumpAllItemModelContents() const; + + protected: + const GeneralSettings * generalSettings; + const ModelData * modelData; + Firmware * firmware; + Boards * board; + Board::Type boardType; + QVector registeredItemModels; +}; + +class FilteredItemModelsFactory +{ + public: + FilteredItemModelsFactory(); + virtual ~FilteredItemModelsFactory(); + + int registerItemModel(FilteredItemModel * itemModel, QString name); + void unregisterItemModels(); + void unregisterItemModel(const int id); + bool isItemModelRegistered(const int id) const; + bool isItemModelRegistered(const QString name) const; + FilteredItemModel * getItemModel(const int id) const; + FilteredItemModel * getItemModel(const QString name) const; + void update(); + + void dumpItemModelContents(const int id) const; + void dumpItemModelContents(const QString name) const; + void dumpAllItemModelContents() const; + + protected: + QVector registeredItemModels; +}; + +#endif // ITEMMODELFACTORY_H diff --git a/companion/src/datamodels/rawitemdatamodels.cpp b/companion/src/datamodels/rawitemdatamodels.cpp index c421ff964..59b1542d5 100644 --- a/companion/src/datamodels/rawitemdatamodels.cpp +++ b/companion/src/datamodels/rawitemdatamodels.cpp @@ -24,35 +24,57 @@ #include "modeldata.h" #include "adjustmentreference.h" -RawSourceItemModel::RawSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +// static +void AbstractItemModel::dumpItemModelContents(AbstractItemModel * itemModel) { - const Boards board = Boards(getCurrentBoard()); - Firmware * fw = getCurrentFirmware(); + if (itemModel) { + qDebug() << "modelid:" << itemModel->getId(); + for (int i = 0; i < itemModel->rowCount(); ++i) { + qDebug() << "row:" << i + << "text:" << itemModel->data(itemModel->index(i, 0), Qt::DisplayRole).toString() + << "id:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemIdRole).toInt() + << "avail:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IsAvailableRole).toBool() + << "flags:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemFlagsRole).toInt() + << "type:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemTypeRole).toInt(); + } + } + else + qDebug() << "Error: model did not cast"; +} + +// +// RawSourceItemModel +// + +RawSourceItemModel::RawSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) +{ + setId(RawSourceId); + setUpdateMask(AllTriggers &~ (CurvesUpdated | ScriptsUpdated)); addItems(SOURCE_TYPE_NONE, RawSource::NoneGroup, 1); - for (int i = 0; i < fw->getCapability(LuaScripts); i++) - addItems(SOURCE_TYPE_LUA_OUTPUT, RawSource::ScriptsGroup, fw->getCapability(LuaOutputsPerScript), i * 16); - addItems(SOURCE_TYPE_VIRTUAL_INPUT, RawSource::InputsGroup, fw->getCapability(VirtualInputs)); - addItems(SOURCE_TYPE_STICK, RawSource::SourcesGroup, board.getCapability(Board::MaxAnalogs)); - addItems(SOURCE_TYPE_ROTARY_ENCODER, RawSource::SourcesGroup, fw->getCapability(RotaryEncoders)); - addItems(SOURCE_TYPE_TRIM, RawSource::TrimsGroup, board.getCapability(Board::NumTrims)); + for (int i = 0; i < firmware->getCapability(LuaScripts); i++) + addItems(SOURCE_TYPE_LUA_OUTPUT, RawSource::ScriptsGroup, firmware->getCapability(LuaOutputsPerScript), i * 16); + addItems(SOURCE_TYPE_VIRTUAL_INPUT, RawSource::InputsGroup, firmware->getCapability(VirtualInputs)); + addItems(SOURCE_TYPE_STICK, RawSource::SourcesGroup, board->getCapability(Board::MaxAnalogs)); + addItems(SOURCE_TYPE_ROTARY_ENCODER, RawSource::SourcesGroup, firmware->getCapability(RotaryEncoders)); + addItems(SOURCE_TYPE_TRIM, RawSource::TrimsGroup, board->getCapability(Board::NumTrims)); addItems(SOURCE_TYPE_MAX, RawSource::SourcesGroup, 1); - addItems(SOURCE_TYPE_SWITCH, RawSource::SwitchesGroup, board.getCapability(Board::Switches)); - addItems(SOURCE_TYPE_CUSTOM_SWITCH, RawSource::SwitchesGroup, fw->getCapability(LogicalSwitches)); + addItems(SOURCE_TYPE_SWITCH, RawSource::SwitchesGroup, board->getCapability(Board::Switches)); + addItems(SOURCE_TYPE_CUSTOM_SWITCH, RawSource::SwitchesGroup, firmware->getCapability(LogicalSwitches)); addItems(SOURCE_TYPE_CYC, RawSource::SourcesGroup, CPN_MAX_CYC); - addItems(SOURCE_TYPE_PPM, RawSource::SourcesGroup, fw->getCapability(TrainerInputs)); - addItems(SOURCE_TYPE_CH, RawSource::SourcesGroup, fw->getCapability(Outputs)); + addItems(SOURCE_TYPE_PPM, RawSource::SourcesGroup, firmware->getCapability(TrainerInputs)); + addItems(SOURCE_TYPE_CH, RawSource::SourcesGroup, firmware->getCapability(Outputs)); addItems(SOURCE_TYPE_SPECIAL, RawSource::TelemGroup, 5); - addItems(SOURCE_TYPE_TELEMETRY, RawSource::TelemGroup, fw->getCapability(Sensors) * 3); - addItems(SOURCE_TYPE_GVAR, RawSource::GVarsGroup, fw->getCapability(Gvars)); + addItems(SOURCE_TYPE_TELEMETRY, RawSource::TelemGroup, firmware->getCapability(Sensors) * 3); + addItems(SOURCE_TYPE_GVAR, RawSource::GVarsGroup, firmware->getCapability(Gvars)); } void RawSourceItemModel::setDynamicItemData(QStandardItem * item, const RawSource & src) const { - Board::Type board = getCurrentBoard(); - item->setText(src.toString(modelData, generalSettings, board)); - item->setData(src.isAvailable(modelData, generalSettings, board), IsAvailableRole); + item->setText(src.toString(modelData, generalSettings, boardType)); + item->setData(src.isAvailable(modelData, generalSettings, boardType), IsAvailableRole); } void RawSourceItemModel::addItems(const RawSourceType & type, const int group, const int count, const int start) @@ -68,49 +90,51 @@ void RawSourceItemModel::addItems(const RawSourceType & type, const int group, c } } -void RawSourceItemModel::update() +void RawSourceItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), RawSource(item(i)->data(ItemIdRole).toInt())); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), RawSource(item(i)->data(ItemIdRole).toInt())); - emit dataUpdateComplete(); + emit updateComplete(); + } } - // // RawSwitchItemModel // -RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - Boards board = Boards(getCurrentBoard()); - Firmware * fw = getCurrentFirmware(); + setId(RawSwitchId); + setUpdateMask(FlightModesUpdated | LogicalSwitchesUpdated | TeleSensorsUpdated); // Descending switch direction: NOT (!) switches addItems(SWITCH_TYPE_ACT, -1); - addItems(SWITCH_TYPE_SENSOR, -fw->getCapability(Sensors)); + addItems(SWITCH_TYPE_SENSOR, -firmware->getCapability(Sensors)); addItems(SWITCH_TYPE_TELEMETRY, -1); - addItems(SWITCH_TYPE_FLIGHT_MODE, -fw->getCapability(FlightModes)); - addItems(SWITCH_TYPE_VIRTUAL, -fw->getCapability(LogicalSwitches)); - addItems(SWITCH_TYPE_ROTARY_ENCODER, -fw->getCapability(RotaryEncoders)); - addItems(SWITCH_TYPE_TRIM, -board.getCapability(Board::NumTrimSwitches)); - addItems(SWITCH_TYPE_MULTIPOS_POT, -(board.getCapability(Board::MultiposPots) * board.getCapability(Board::MultiposPotsPositions))); - addItems(SWITCH_TYPE_SWITCH, -board.getCapability(Board::SwitchPositions)); + addItems(SWITCH_TYPE_FLIGHT_MODE, -firmware->getCapability(FlightModes)); + addItems(SWITCH_TYPE_VIRTUAL, -firmware->getCapability(LogicalSwitches)); + addItems(SWITCH_TYPE_ROTARY_ENCODER, -firmware->getCapability(RotaryEncoders)); + addItems(SWITCH_TYPE_TRIM, -board->getCapability(Board::NumTrimSwitches)); + addItems(SWITCH_TYPE_MULTIPOS_POT, -(board->getCapability(Board::MultiposPots) * board->getCapability(Board::MultiposPotsPositions))); + 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)); - addItems(SWITCH_TYPE_TRIM, board.getCapability(Board::NumTrimSwitches)); - addItems(SWITCH_TYPE_ROTARY_ENCODER, fw->getCapability(RotaryEncoders)); - addItems(SWITCH_TYPE_VIRTUAL, fw->getCapability(LogicalSwitches)); - addItems(SWITCH_TYPE_FLIGHT_MODE, fw->getCapability(FlightModes)); + addItems(SWITCH_TYPE_SWITCH, board->getCapability(Board::SwitchPositions)); + addItems(SWITCH_TYPE_MULTIPOS_POT, board->getCapability(Board::MultiposPots) * board->getCapability(Board::MultiposPotsPositions)); + addItems(SWITCH_TYPE_TRIM, board->getCapability(Board::NumTrimSwitches)); + addItems(SWITCH_TYPE_ROTARY_ENCODER, firmware->getCapability(RotaryEncoders)); + addItems(SWITCH_TYPE_VIRTUAL, firmware->getCapability(LogicalSwitches)); + addItems(SWITCH_TYPE_FLIGHT_MODE, firmware->getCapability(FlightModes)); addItems(SWITCH_TYPE_TELEMETRY, 1); - addItems(SWITCH_TYPE_SENSOR, fw->getCapability(Sensors)); + addItems(SWITCH_TYPE_SENSOR, firmware->getCapability(Sensors)); addItems(SWITCH_TYPE_ON, 1); addItems(SWITCH_TYPE_ONE, 1); addItems(SWITCH_TYPE_ACT, 1); @@ -118,9 +142,8 @@ RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSett void RawSwitchItemModel::setDynamicItemData(QStandardItem * item, const RawSwitch & rsw) const { - const Board::Type board = getCurrentBoard(); - item->setText(rsw.toString(board, generalSettings, modelData)); - item->setData(rsw.isAvailable(modelData, generalSettings, board), IsAvailableRole); + item->setText(rsw.toString(boardType, generalSettings, modelData)); + item->setData(rsw.isAvailable(modelData, generalSettings, boardType), IsAvailableRole); } void RawSwitchItemModel::addItems(const RawSwitchType & type, int count) @@ -171,27 +194,34 @@ void RawSwitchItemModel::addItems(const RawSwitchType & type, int count) } } -void RawSwitchItemModel::update() +void RawSwitchItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), RawSwitch(item(i)->data(ItemIdRole).toInt())); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), RawSwitch(item(i)->data(ItemIdRole).toInt())); - emit dataUpdateComplete(); + emit updateComplete(); + } } // // CurveItemModel // -CurveItemModel::CurveItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +CurveItemModel::CurveItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(CurveId); + if (!modelData) return; - const int count = getCurrentFirmware()->getCapability(NumCurves); + setUpdateMask(CurvesUpdated); + + const int count = firmware->getCapability(NumCurves); for (int i = -count ; i <= count; ++i) { QStandardItem * modelItem = new QStandardItem(); @@ -205,35 +235,42 @@ CurveItemModel::CurveItemModel(const GeneralSettings * const generalSettings, co } } -void CurveItemModel::setDynamicItemData(QStandardItem * item, const int index) const +void CurveItemModel::setDynamicItemData(QStandardItem * item, const int value) const { - CurveReference cr = CurveReference(CurveReference::CURVE_REF_CUSTOM, index); + CurveReference cr = CurveReference(CurveReference::CURVE_REF_CUSTOM, value); item->setText(cr.toString(modelData, false)); item->setData(cr.isAvailable(), IsAvailableRole); } -void CurveItemModel::update() +void CurveItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); - emit dataUpdateComplete(); + emit updateComplete(); + } } - // // GVarReferenceItemModel // -GVarReferenceItemModel::GVarReferenceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +GVarReferenceItemModel::GVarReferenceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(GVarRefId); + if (!modelData) return; - const int count = getCurrentFirmware()->getCapability(Gvars); + setUpdateMask(GVarsUpdated | FlightModesUpdated | LogicalSwitchesUpdated); + + const int count = firmware->getCapability(Gvars); + if (count > 0) { addItems(-count); addItems(count); @@ -260,27 +297,33 @@ void GVarReferenceItemModel::setDynamicItemData(QStandardItem * item, const Adju item->setData(ar.isAvailable(), IsAvailableRole); } -void GVarReferenceItemModel::update() +void GVarReferenceItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), AdjustmentReference(item(i)->data(ItemIdRole).toInt())); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), AdjustmentReference(item(i)->data(ItemIdRole).toInt())); - emit dataUpdateComplete(); + emit updateComplete(); + } } - // // ThrottleSourceItemModel // -ThrottleSourceItemModel::ThrottleSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +ThrottleSourceItemModel::ThrottleSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(ThrSourceId); + if (!modelData) return; + setUpdateMask(TimersUpdated | InputsUpdated | TeleSensorsUpdated); + for (int i = 0; i < modelData->thrTraceSrcCount(); i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setData(i, ItemIdRole); @@ -289,30 +332,35 @@ ThrottleSourceItemModel::ThrottleSourceItemModel(const GeneralSettings * const g } } -void ThrottleSourceItemModel::setDynamicItemData(QStandardItem * item, const int index) const +void ThrottleSourceItemModel::setDynamicItemData(QStandardItem * item, const int value) const { - item->setText(modelData->thrTraceSrcToString(index)); - item->setData(modelData->isThrTraceSrcAvailable(generalSettings, index), IsAvailableRole); + item->setText(modelData->thrTraceSrcToString(value)); + item->setData(modelData->isThrTraceSrcAvailable(generalSettings, value), IsAvailableRole); } -void ThrottleSourceItemModel::update() +void ThrottleSourceItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); - emit dataUpdateComplete(); + emit updateComplete(); + } } - // // CustomFuncActionItemModel // -CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(CustomFuncActionId); + setUpdateMask(AllTriggers &~ (CurvesUpdated | ScriptsUpdated)); + for (int i = 0; i < AssignFunc::FuncCount; i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setData(i, ItemIdRole); @@ -322,30 +370,35 @@ CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * con } } -void CustomFuncActionItemModel::setDynamicItemData(QStandardItem * item, const int index) const +void CustomFuncActionItemModel::setDynamicItemData(QStandardItem * item, const int value) const { - item->setText(CustomFunctionData(AssignFunc(index)).funcToString(modelData)); - item->setData(CustomFunctionData::isFuncAvailable(index), IsAvailableRole); + item->setText(CustomFunctionData(AssignFunc(value)).funcToString(modelData)); + item->setData(CustomFunctionData::isFuncAvailable(value), IsAvailableRole); } -void CustomFuncActionItemModel::update() +void CustomFuncActionItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); - emit dataUpdateComplete(); + emit updateComplete(); + } } - // // CustomFuncResetParamItemModel // -CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(CustomFuncResetParamId); + setUpdateMask(TeleSensorsUpdated); + for (int i = 0; i < CustomFunctionData::resetParamCount(modelData); i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setData(i, ItemIdRole); @@ -354,38 +407,43 @@ CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettin } } -void CustomFuncResetParamItemModel::setDynamicItemData(QStandardItem * item, const int index) const +void CustomFuncResetParamItemModel::setDynamicItemData(QStandardItem * item, const int value) const { CustomFunctionData cfd = CustomFunctionData(AssignFunc::FuncReset); - cfd.param = index; + cfd.param = value; item->setText(cfd.paramToString(modelData)); - item->setData(CustomFunctionData::isResetParamAvailable(modelData, index), IsAvailableRole); + item->setData(CustomFunctionData::isResetParamAvailable(modelData, value), IsAvailableRole); } -void CustomFuncResetParamItemModel::update() +void CustomFuncResetParamItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); - emit dataUpdateComplete(); + emit updateComplete(); + } } - // // TelemetrySourceItemModel // -TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(TeleSourceId); + if (!modelData) return; - const int count = getCurrentFirmware()->getCapability(Sensors); + setUpdateMask(TeleSensorsUpdated); + const int count = firmware->getCapability(Sensors); - for (int i = -count + 1; i < count; ++i) { + for (int i = -count; i <= count; ++i) { QStandardItem * modelItem = new QStandardItem(); modelItem->setData(i, ItemIdRole); int flags = i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup; @@ -397,118 +455,35 @@ TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const } } -void TelemetrySourceItemModel::setDynamicItemData(QStandardItem * item, const int index) const +void TelemetrySourceItemModel::setDynamicItemData(QStandardItem * item, const int value) const { - item->setText(SensorData::sourceToString(modelData, index)); - item->setData(SensorData::isSourceAvailable(modelData, index), IsAvailableRole); + item->setText(SensorData::sourceToString(modelData, value)); + item->setData(SensorData::isSourceAvailable(modelData, value), IsAvailableRole); } -void TelemetrySourceItemModel::update() +void TelemetrySourceItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); - - emit dataUpdateComplete(); -} - - -// -// CommonItemModels TODO rename to DynamicDataModels -// - -CommonItemModels::CommonItemModels(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - QObject(parent) -{ - m_rawSourceItemModel = new RawSourceItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("raw source"), qobject_cast(m_rawSourceItemModel)); - - m_rawSwitchItemModel = new RawSwitchItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("raw switch"), qobject_cast(m_rawSwitchItemModel)); - - m_curveItemModel = new CurveItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("curve"), qobject_cast(m_curveItemModel)); - - m_gvarReferenceItemModel = new GVarReferenceItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("gvar ref"), qobject_cast(m_gvarReferenceItemModel)); - - m_throttleSourceItemModel = new ThrottleSourceItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("thr source"), qobject_cast(m_throttleSourceItemModel)); - - m_customFuncActionItemModel = new CustomFuncActionItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("cf action"), qobject_cast(m_customFuncActionItemModel)); - - m_customFuncResetParamItemModel = new CustomFuncResetParamItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("cf reset param"), qobject_cast(m_customFuncResetParamItemModel)); - - m_telemetrySourceItemModel = new TelemetrySourceItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("tele source"), qobject_cast(m_telemetrySourceItemModel)); -} - -CommonItemModels::~CommonItemModels() -{ -} - -void CommonItemModels::update(const RadioModelObjects radioModelObjects) -{ - switch (radioModelObjects) { - case RMO_CHANNELS: - m_throttleSourceItemModel->update(); - // no break - case RMO_TIMERS: - m_customFuncActionItemModel->update(); - // no break - case RMO_INPUTS: - case RMO_TELEMETRY_SENSORS: - m_rawSourceItemModel->update(); - m_customFuncResetParamItemModel->update(); - m_telemetrySourceItemModel->update(); - break; - - case RMO_GLOBAL_VARIABLES: - m_gvarReferenceItemModel->update(); - m_customFuncActionItemModel->update(); - // no break - case RMO_FLIGHT_MODES: - case RMO_LOGICAL_SWITCHES: - m_rawSourceItemModel->update(); - m_rawSwitchItemModel->update(); - break; - - case RMO_CURVES: - m_curveItemModel->update(); - break; - - case RMO_SCRIPTS: - // no need to refresh - break; - - default: - qDebug() << "Unknown RadioModelObject:" << radioModelObjects; - } -} - - -void dumpModelContents(QString desc, AbstractRawItemDataModel * dataModel) -{ - if (dataModel) { - qDebug() << "model:" << desc << "rows:" << dataModel->rowCount(); - for (int i = 0; i < dataModel->rowCount(); ++i) { - qDebug() << "row:" << i << "text:" << dataModel->item(i)->data(Qt::DisplayRole).toString() - << "id:" << dataModel->item(i)->data(AbstractRawItemDataModel::ItemIdRole).toInt() - << "avail:" << dataModel->item(i)->data(AbstractRawItemDataModel::IsAvailableRole).toBool() - << "flags:" << dataModel->item(i)->data(AbstractRawItemDataModel::ItemFlagsRole).toInt() - << "type:" << dataModel->item(i)->data(AbstractRawItemDataModel::ItemTypeRole).toInt(); + for (int i = 0; i < rowCount(); ++i) { + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); } + + emit updateComplete(); } - else - qDebug() << "model:" << desc << "did not cast"; } -CurveRefTypeItemModel::CurveRefTypeItemModel(QObject * parent) : - AbstractStandardItemModel(parent) +// +// CurveRefTypeItemModel +// + +CurveRefTypeItemModel::CurveRefTypeItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractStaticItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(CurveRefTypeId); + for (int i = 0; i <= CurveReference::MAX_CURVE_REF_TYPE; i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setText(CurveReference::typeToString((CurveReference::CurveRefType)i)); @@ -518,9 +493,16 @@ CurveRefTypeItemModel::CurveRefTypeItemModel(QObject * parent) : } } -CurveRefFuncItemModel::CurveRefFuncItemModel(QObject * parent) : - AbstractStandardItemModel(parent) +// +// CurveRefFuncItemModel +// + +CurveRefFuncItemModel::CurveRefFuncItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractStaticItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(CurveRefFuncId); + for (int i = 1; i <= CurveReference::functionCount(); i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setText(CurveReference::functionToString(i)); @@ -529,3 +511,106 @@ CurveRefFuncItemModel::CurveRefFuncItemModel(QObject * parent) : appendRow(modelItem); } } + +// +// ItemModelsFactory +// + +ItemModelsFactory::ItemModelsFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData) : + generalSettings(generalSettings), + modelData(modelData) +{ + firmware = getCurrentFirmware(); + board = new Boards(getCurrentBoard()); + boardType = getCurrentBoard(); +} + +ItemModelsFactory::~ItemModelsFactory() +{ + unregisterItemModels(); + delete board; +} + +void ItemModelsFactory::addItemModel(const AbstractItemModel::ItemModelId id) +{ + switch (id) { + case AbstractItemModel::RawSourceId: + registerItemModel(new RawSourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::RawSwitchId: + registerItemModel(new RawSwitchItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CurveId: + registerItemModel(new CurveItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::GVarRefId: + registerItemModel(new GVarReferenceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::ThrSourceId: + registerItemModel(new ThrottleSourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CustomFuncActionId: + registerItemModel(new CustomFuncActionItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CustomFuncResetParamId: + registerItemModel(new CustomFuncResetParamItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::TeleSourceId: + registerItemModel(new TelemetrySourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CurveRefTypeId: + registerItemModel(new CurveRefTypeItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CurveRefFuncId: + registerItemModel(new CurveRefFuncItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + default: + qDebug() << "Error: unknown item model: id"; + break; + } +} + +void ItemModelsFactory::registerItemModel(AbstractItemModel * itemModel) +{ + if (itemModel && !getItemModel(itemModel->getId())) { + registeredItemModels.push_back(itemModel); + } +} + +void ItemModelsFactory::unregisterItemModels() +{ + foreach (AbstractItemModel *itemModel, registeredItemModels) { + delete itemModel; + } +} + +void ItemModelsFactory::unregisterItemModel(const AbstractItemModel::ItemModelId id) +{ + AbstractItemModel * itemModel = getItemModel(id); + if (itemModel) + delete itemModel; +} + +AbstractItemModel * ItemModelsFactory::getItemModel(const AbstractItemModel::ItemModelId id) const +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + if (itemModel->getId() == id) + return itemModel; + } + + return nullptr; +} + +void ItemModelsFactory::update(const AbstractItemModel::UpdateTrigger trigger) +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + itemModel->update(trigger); + } +} + +void ItemModelsFactory::dumpAllItemModelContents() const +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + AbstractItemModel::dumpItemModelContents(itemModel); + } +} diff --git a/companion/src/datamodels/rawitemdatamodels.h b/companion/src/datamodels/rawitemdatamodels.h index 8c8a78cc4..89f24acdc 100644 --- a/companion/src/datamodels/rawitemdatamodels.h +++ b/companion/src/datamodels/rawitemdatamodels.h @@ -30,13 +30,24 @@ class GeneralSettings; class ModelData; class AdjustmentReference; -class AbstractStandardItemModel: public QStandardItemModel +class AbstractItemModel: public QStandardItemModel { Q_OBJECT public: - explicit AbstractStandardItemModel(QObject * parent = nullptr) : - QStandardItemModel(parent) - {} + enum ItemModelId { + UnknownId, + RawSourceId, + RawSwitchId, + CurveId, + GVarRefId, + ThrSourceId, + CustomFuncActionId, + CustomFuncResetParamId, + TeleSourceId, + CurveRefTypeId, + CurveRefFuncId + }; + Q_ENUM(ItemModelId) enum DataRoles { ItemIdRole = Qt::UserRole, @@ -52,203 +63,254 @@ class AbstractStandardItemModel: public QStandardItemModel PositiveGroup = 0x04 }; Q_ENUM(DataGroups) -}; -class AbstractRawItemDataModel: public AbstractStandardItemModel -{ - Q_OBJECT - public: - explicit AbstractRawItemDataModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr) : - AbstractStandardItemModel(parent), + enum UpdateTrigger { + SystemRefresh = 1 << 0, + ChannelsUpdated = 1 << 1, + CurvesUpdated = 1 << 2, + FlightModesUpdated = 1 << 3, + GVarsUpdated = 1 << 4, + InputsUpdated = 1 << 5, + LogicalSwitchesUpdated = 1 << 6, + ScriptsUpdated = 1 << 7, + TeleSensorsUpdated = 1 << 8, + TimersUpdated = 1 << 9, + AllTriggers = SystemRefresh | ChannelsUpdated | CurvesUpdated | FlightModesUpdated | GVarsUpdated | InputsUpdated | + LogicalSwitchesUpdated | ScriptsUpdated | TeleSensorsUpdated | TimersUpdated, + NoTriggers = 0 + }; + Q_ENUM(UpdateTrigger) + + explicit AbstractItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + QStandardItemModel(nullptr), generalSettings(generalSettings), - modelData(modelData) + modelData(modelData), + firmware(firmware), + board(board), + boardType(boardType) {} - public slots: - virtual void update() = 0; + virtual ~AbstractItemModel() {}; - signals: - void dataAboutToBeUpdated(); - void dataUpdateComplete(); + void setId(ItemModelId id) { m_id = id; } + ItemModelId getId () const { return m_id; } + + void setUpdateMask(const int mask) { m_updateMask = mask; } + int getUpdateMask() const { return m_updateMask; } + inline bool doUpdate(const UpdateTrigger trigger) const { return m_updateMask & (int)trigger; } + + AbstractItemModel * getItemModel(const ItemModelId id) const; + + static void dumpItemModelContents(AbstractItemModel * itemModel); + + public slots: + virtual void update(const UpdateTrigger trigger = SystemRefresh) = 0; protected: const GeneralSettings * generalSettings; const ModelData * modelData; + Firmware * firmware; + const Boards * board; + const Board::Type boardType; + + private: + ItemModelId m_id = UnknownId; + int m_updateMask = 0; }; - -class RawSourceItemModel: public AbstractRawItemDataModel +class AbstractStaticItemModel: public AbstractItemModel { Q_OBJECT public: - explicit RawSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit AbstractStaticItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractItemModel(generalSettings, modelData, firmware, board, boardType) {} + virtual ~AbstractStaticItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override final {} +}; + +class AbstractDynamicItemModel: public AbstractItemModel +{ + Q_OBJECT + public: + explicit AbstractDynamicItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractItemModel(generalSettings, modelData, firmware, board, boardType) {} + virtual ~AbstractDynamicItemModel() {}; + + public slots: + virtual void update(const UpdateTrigger trigger = SystemRefresh) {} + + signals: + void aboutToBeUpdated(); + void updateComplete(); +}; + +class RawSourceItemModel: public AbstractDynamicItemModel +{ + Q_OBJECT + public: + explicit RawSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~RawSourceItemModel() {}; + + public slots: + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const RawSource & src) const; + virtual void setDynamicItemData(QStandardItem * item, const RawSource & src) const; void addItems(const RawSourceType & type, const int group, const int count, const int start = 0); }; - -class RawSwitchItemModel: public AbstractRawItemDataModel +class RawSwitchItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit RawSwitchItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit RawSwitchItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~RawSwitchItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const RawSwitch & rsw) const; + virtual void setDynamicItemData(QStandardItem * item, const RawSwitch & rsw) const; void addItems(const RawSwitchType & type, int count); }; - -class CurveItemModel: public AbstractRawItemDataModel +class CurveItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit CurveItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit CurveItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~CurveItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const int index) const; + virtual void setDynamicItemData(QStandardItem * item, const int value) const; }; - -class GVarReferenceItemModel: public AbstractRawItemDataModel +class GVarReferenceItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit GVarReferenceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit GVarReferenceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~GVarReferenceItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const AdjustmentReference & ar) const; + virtual void setDynamicItemData(QStandardItem * item, const AdjustmentReference & ar) const; void addItems(int count); }; - -class ThrottleSourceItemModel: public AbstractRawItemDataModel +class ThrottleSourceItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit ThrottleSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit ThrottleSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~ThrottleSourceItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const int index) const; + virtual void setDynamicItemData(QStandardItem * item, const int value) const; }; - -class CustomFuncActionItemModel: public AbstractRawItemDataModel +class CustomFuncActionItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit CustomFuncActionItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit CustomFuncActionItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~CustomFuncActionItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const int index) const; + virtual void setDynamicItemData(QStandardItem * item, const int value) const; }; - -class CustomFuncResetParamItemModel: public AbstractRawItemDataModel +class CustomFuncResetParamItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit CustomFuncResetParamItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit CustomFuncResetParamItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~CustomFuncResetParamItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const int index) const; + virtual void setDynamicItemData(QStandardItem * item, const int value) const; }; - -class TelemetrySourceItemModel: public AbstractRawItemDataModel +class TelemetrySourceItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit TelemetrySourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit TelemetrySourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~TelemetrySourceItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const int index) const; + virtual void setDynamicItemData(QStandardItem * item, const int value) const; }; - -class CommonItemModels: public QObject +class CurveRefTypeItemModel : public AbstractStaticItemModel { Q_OBJECT public: - enum RadioModelObjects { - RMO_CHANNELS, - RMO_CURVES, - RMO_FLIGHT_MODES, - RMO_GLOBAL_VARIABLES, - RMO_INPUTS, - RMO_LOGICAL_SWITCHES, - RMO_SCRIPTS, - RMO_TELEMETRY_SENSORS, - RMO_TIMERS - }; - Q_ENUM(RadioModelObjects) - - explicit CommonItemModels(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); - ~CommonItemModels(); - - void update(const RadioModelObjects radioModelObjects); - RawSourceItemModel * rawSourceItemModel() const { return m_rawSourceItemModel; } - RawSwitchItemModel * rawSwitchItemModel() const { return m_rawSwitchItemModel; } - CurveItemModel * curveItemModel() const { return m_curveItemModel; } - GVarReferenceItemModel * gvarItemModel() const { return m_gvarReferenceItemModel; } - ThrottleSourceItemModel * throttleSourceItemModel() const { return m_throttleSourceItemModel; } - CustomFuncActionItemModel * customFuncActionItemModel() const { return m_customFuncActionItemModel; } - CustomFuncResetParamItemModel * customFuncResetParamItemModel() const { return m_customFuncResetParamItemModel; } - TelemetrySourceItemModel * telemetrySourceItemModel() const { return m_telemetrySourceItemModel; } - - private: - RawSourceItemModel *m_rawSourceItemModel; - RawSwitchItemModel *m_rawSwitchItemModel; - CurveItemModel *m_curveItemModel; - GVarReferenceItemModel *m_gvarReferenceItemModel; - ThrottleSourceItemModel *m_throttleSourceItemModel; - CustomFuncActionItemModel *m_customFuncActionItemModel; - CustomFuncResetParamItemModel *m_customFuncResetParamItemModel; - TelemetrySourceItemModel *m_telemetrySourceItemModel; + explicit CurveRefTypeItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~CurveRefTypeItemModel() {}; }; -void dumpModelContents(QString desc, AbstractRawItemDataModel * dataModel); - - -class CurveRefTypeItemModel : public AbstractStandardItemModel +class CurveRefFuncItemModel : public AbstractStaticItemModel { Q_OBJECT public: - explicit CurveRefTypeItemModel(QObject * parent = nullptr); + explicit CurveRefFuncItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~CurveRefFuncItemModel() {}; }; -class CurveRefFuncItemModel : public AbstractStandardItemModel +class ItemModelsFactory { - Q_OBJECT public: - explicit CurveRefFuncItemModel(QObject * parent = nullptr); -}; + ItemModelsFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData); + virtual ~ItemModelsFactory(); + void addItemModel(const AbstractItemModel::ItemModelId id); + void registerItemModel(AbstractItemModel * itemModel); + void unregisterItemModels(); + void unregisterItemModel(const AbstractItemModel::ItemModelId id); + AbstractItemModel * getItemModel(const AbstractItemModel::ItemModelId id) const; + void update(const AbstractItemModel::UpdateTrigger trigger = AbstractItemModel::SystemRefresh); + void dumpAllItemModelContents() const; + + protected: + const GeneralSettings * generalSettings; + const ModelData * modelData; + Firmware * firmware; + Boards * board; + Board::Type boardType; + QVector registeredItemModels; +}; #endif // RAWITEMDATAMODELS_H diff --git a/companion/src/datamodels/rawitemfilteredmodel.cpp b/companion/src/datamodels/rawitemfilteredmodel.cpp index abe9ca08e..1cddff04d 100644 --- a/companion/src/datamodels/rawitemfilteredmodel.cpp +++ b/companion/src/datamodels/rawitemfilteredmodel.cpp @@ -20,24 +20,24 @@ #include "rawitemfilteredmodel.h" -RawItemFilteredModel::RawItemFilteredModel(QAbstractItemModel * sourceModel, int flags, QObject * parent) : - QSortFilterProxyModel(parent), +FilteredItemModel::FilteredItemModel(AbstractItemModel * sourceModel, int flags) : + QSortFilterProxyModel(nullptr), filterFlags(0) { - setFilterRole(AbstractRawItemDataModel::IsAvailableRole); + setFilterRole(AbstractItemModel::IsAvailableRole); setFilterKeyColumn(0); setFilterFlags(flags); setDynamicSortFilter(true); setSourceModel(sourceModel); - AbstractRawItemDataModel * itemModel = qobject_cast(sourceModel); + AbstractDynamicItemModel * itemModel = qobject_cast(sourceModel); if (itemModel) { - connect(itemModel, &AbstractRawItemDataModel::dataAboutToBeUpdated, this, &RawItemFilteredModel::onDataAboutToBeUpdated); - connect(itemModel, &AbstractRawItemDataModel::dataUpdateComplete, this, &RawItemFilteredModel::onDataUpdateComplete); + connect(itemModel, &AbstractDynamicItemModel::aboutToBeUpdated, this, &FilteredItemModel::onAboutToBeUpdated); + connect(itemModel, &AbstractDynamicItemModel::updateComplete, this, &FilteredItemModel::onUpdateComplete); } } -void RawItemFilteredModel::setFilterFlags(int flags) +void FilteredItemModel::setFilterFlags(int flags) { if (filterFlags != flags) { filterFlags = flags; @@ -45,7 +45,7 @@ void RawItemFilteredModel::setFilterFlags(int flags) } } -bool RawItemFilteredModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const +bool FilteredItemModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const { const QModelIndex & srcIdx = sourceModel()->index(sourceRow, 0, sourceParent); if (!srcIdx.isValid() || !sourceModel()->data(srcIdx, filterRole()).toBool()) @@ -55,23 +55,137 @@ bool RawItemFilteredModel::filterAcceptsRow(int sourceRow, const QModelIndex & s return true; bool ok; - const int flags = sourceModel()->data(srcIdx, AbstractRawItemDataModel::ItemFlagsRole).toInt(&ok); + const int flags = sourceModel()->data(srcIdx, AbstractItemModel::ItemFlagsRole).toInt(&ok); return (ok && (!flags || (filterFlags & flags))); } -void RawItemFilteredModel::update() const +void FilteredItemModel::update() const { - AbstractRawItemDataModel * itemModel = qobject_cast(sourceModel()); + AbstractDynamicItemModel * itemModel = qobject_cast(sourceModel()); if (itemModel) itemModel->update(); } -void RawItemFilteredModel::onDataAboutToBeUpdated() +void FilteredItemModel::onAboutToBeUpdated() { - emit dataAboutToBeUpdated(); + emit aboutToBeUpdated(); } -void RawItemFilteredModel::onDataUpdateComplete() +void FilteredItemModel::onUpdateComplete() { - emit dataUpdateComplete(); + emit updateComplete(); +} + +// static +void FilteredItemModel::dumpItemModelContents(FilteredItemModel * itemModel) +{ + if (itemModel) { + qDebug() << "id:" << itemModel->getId() << "name:" << itemModel->getName(); + + for (int i = 0; i < itemModel->rowCount(); ++i) { + qDebug() << "row:" << i + << "text:" << itemModel->data(itemModel->index(i, 0), Qt::DisplayRole).toString() + << "id:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemIdRole).toInt() + << "avail:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IsAvailableRole).toBool() + << "flags:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemFlagsRole).toInt() + << "type:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemTypeRole).toInt(); + } + } + else + qDebug() << "Error: model did not cast"; +} + +// +// FilteredItemModelsFactory +// + +FilteredItemModelsFactory::FilteredItemModelsFactory() +{ +} + +FilteredItemModelsFactory::~FilteredItemModelsFactory() +{ + unregisterItemModels(); +} + +int FilteredItemModelsFactory::registerItemModel(FilteredItemModel * itemModel, QString name) +{ + if (itemModel) { + if (isItemModelRegistered(name)) + qDebug() << "Warning: item model already registered with name:" << name; + const int id = registeredItemModels.count(); + itemModel->setId(id); + itemModel->setName(name); + registeredItemModels.push_back(itemModel); + return id; + } + return 0; +} + +void FilteredItemModelsFactory::unregisterItemModels() +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + delete itemModel; + } +} + +void FilteredItemModelsFactory::unregisterItemModel(const int id) +{ + FilteredItemModel * itemModel = getItemModel(id); + if (itemModel) + delete itemModel; +} + +bool FilteredItemModelsFactory::isItemModelRegistered(const int id) const +{ + return getItemModel(id); +} + +bool FilteredItemModelsFactory::isItemModelRegistered(const QString name) const +{ + return getItemModel(name); +} + +FilteredItemModel * FilteredItemModelsFactory::getItemModel(const int id) const +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + if (itemModel->getId() == id) + return itemModel; + } + + return nullptr; +} + +FilteredItemModel * FilteredItemModelsFactory::getItemModel(const QString name) const +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + if (itemModel->getName() == name) + return itemModel; + } + + return nullptr; +} + +void FilteredItemModelsFactory::update() +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + itemModel->update(); + } +} + +void FilteredItemModelsFactory::dumpItemModelContents(const int id) const +{ + FilteredItemModel::dumpItemModelContents(getItemModel(id)); +} + +void FilteredItemModelsFactory::dumpItemModelContents(const QString name) const +{ + FilteredItemModel::dumpItemModelContents(getItemModel(name)); +} + +void FilteredItemModelsFactory::dumpAllItemModelContents() const +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + FilteredItemModel::dumpItemModelContents(itemModel); + } } diff --git a/companion/src/datamodels/rawitemfilteredmodel.h b/companion/src/datamodels/rawitemfilteredmodel.h index 3274460f7..08bc65993 100644 --- a/companion/src/datamodels/rawitemfilteredmodel.h +++ b/companion/src/datamodels/rawitemfilteredmodel.h @@ -23,41 +23,90 @@ #include "rawitemdatamodels.h" +#include #include +#include -class GeneralSettings; -class ModelData; - -class RawItemFilteredModel: public QSortFilterProxyModel +class FilteredItemModel: public QSortFilterProxyModel { Q_OBJECT public: enum DataFilters { - AllFilter = AbstractRawItemDataModel::NegativeGroup | AbstractRawItemDataModel::NoneGroup | AbstractRawItemDataModel::PositiveGroup, - AllExcludeNoneFilter = AllFilter &~ AbstractRawItemDataModel::NoneGroup, - NegativeFilter = AbstractRawItemDataModel::NegativeGroup | AbstractRawItemDataModel::NoneGroup, - NegativeExcludeNoneFilter = AbstractRawItemDataModel::NegativeGroup, - PositiveFilter = AbstractRawItemDataModel::PositiveGroup | AbstractRawItemDataModel::NoneGroup, - PositiveExcludeNoneFilter = AbstractRawItemDataModel::PositiveGroup + AllFilter = AbstractItemModel::NegativeGroup | AbstractItemModel::NoneGroup | AbstractItemModel::PositiveGroup, + AllExcludeNoneFilter = AllFilter &~ AbstractItemModel::NoneGroup, + NegativeFilter = AbstractItemModel::NegativeGroup | AbstractItemModel::NoneGroup, + NegativeExcludeNoneFilter = AbstractItemModel::NegativeGroup, + PositiveFilter = AbstractItemModel::PositiveGroup | AbstractItemModel::NoneGroup, + PositiveExcludeNoneFilter = AbstractItemModel::PositiveGroup }; Q_ENUM(DataFilters) - explicit RawItemFilteredModel(QAbstractItemModel * sourceModel, int flags, QObject * parent = nullptr); - explicit RawItemFilteredModel(QAbstractItemModel * sourceModel, QObject * parent = nullptr) : RawItemFilteredModel(sourceModel, 0, parent) {} + explicit FilteredItemModel(AbstractItemModel * sourceModel, int flags); + explicit FilteredItemModel(AbstractItemModel * sourceModel) : + FilteredItemModel(sourceModel, 0) {} + virtual ~FilteredItemModel() {}; + + void setId(int id) { m_id = id; } + int getId() const { return m_id; }; + void setName(QString name) { m_name = name; } + QString getName() const { return m_name; } + + static void dumpItemModelContents(FilteredItemModel * itemModel); public slots: void setFilterFlags(int flags); void update() const; - void onDataAboutToBeUpdated(); - void onDataUpdateComplete(); + void onAboutToBeUpdated(); + void onUpdateComplete(); signals: - void dataAboutToBeUpdated(); - void dataUpdateComplete(); + void aboutToBeUpdated(); + void updateComplete(); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const override; int filterFlags; + + private: + int m_id = 0; + QString m_name = ""; }; + +class FilteredItemModelsFactory +{ + public: + FilteredItemModelsFactory(); + virtual ~FilteredItemModelsFactory(); + + int registerItemModel(FilteredItemModel * itemModel, QString name); + void unregisterItemModels(); + void unregisterItemModel(const int id); + bool isItemModelRegistered(const int id) const; + bool isItemModelRegistered(const QString name) const; + FilteredItemModel * getItemModel(const int id) const; + FilteredItemModel * getItemModel(const QString name) const; + void update(); + + void dumpItemModelContents(const int id) const; + void dumpItemModelContents(const QString name) const; + void dumpAllItemModelContents() const; + + protected: + QVector registeredItemModels; +}; + + +#define FILTEREDITEMMODELCONNECT(fltrmodel) \ + connect(fltrmodel, SIGNAL(aboutToBeUpdated()), this, SLOT(onItemModelAboutToBeUpdated())); \ + connect(fltrmodel, SIGNAL(updateComplete()), this, SLOT(onItemModelUpdateComplete())); + +#define FILTEREDITEMMODEL(fltrmodel, class, modelid, flags) \ + fltrmodel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::modelid), flags); \ + FILTEREDITEMMODELCONNECT(fltrmodel) + +#define FILTEREDITEMMODELNOFLAGS(fltrmodel, class, modelid) \ + fltrmodel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::modelid)); \ + FILTEREDITEMMODELCONNECT(fltrmodel) + #endif // RAWITEMFILTEREDMODEL_H diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 688b4188f..4d961630a 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -144,37 +144,41 @@ int CurveReference::functionCount() * CurveReferenceUIManager */ -CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveValueCB, CurveReference & curve, const ModelData & model, - RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel, - QObject * parent) : +CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveValueCB, CurveReference & curveRef, const ModelData & model, + FilteredItemModel * curveItemModel, FilteredItemModel * gvarItemModel, + FilteredItemModel * typeItemModel, FilteredItemModel * funcItemModel, QObject * parent) : QObject(parent), curveTypeCB(nullptr), curveGVarCB(nullptr), curveValueSB(nullptr), curveValueCB(curveValueCB), - curve(curve), + curveRef(curveRef), model(model), lock(false), curveItemModel(curveItemModel), - gvarItemModel(gvarItemModel) + gvarItemModel(gvarItemModel), + typeItemModel(typeItemModel), + funcItemModel(funcItemModel) { init(parent); } CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheckBox * curveGVarCB, QSpinBox * curveValueSB, - QComboBox * curveValueCB, CurveReference & curve, const ModelData & model, - RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel, - QObject * parent) : + QComboBox * curveValueCB, CurveReference & curveRef, const ModelData & model, + FilteredItemModel * curveItemModel, FilteredItemModel * gvarItemModel, + FilteredItemModel * typeItemModel, FilteredItemModel * funcItemModel, QObject * parent) : QObject(parent), curveTypeCB(curveTypeCB), curveGVarCB(curveGVarCB), curveValueSB(curveValueSB), curveValueCB(curveValueCB), - curve(curve), + curveRef(curveRef), model(model), lock(false), curveItemModel(curveItemModel), - gvarItemModel(gvarItemModel) + gvarItemModel(gvarItemModel), + typeItemModel(typeItemModel), + funcItemModel(funcItemModel) { init(parent); } @@ -188,9 +192,8 @@ void CurveReferenceUIManager::init(QObject * parent) hasCapabilityGvars = getCurrentFirmware()->getCapability(Gvars); if (curveTypeCB) { - typeItemModel = new RawItemFilteredModel(new CurveRefTypeItemModel(parent), parent); curveTypeCB->setModel(typeItemModel); - curveTypeCB->setCurrentIndex(curveTypeCB->findData((int)curve.type)); + curveTypeCB->setCurrentIndex(curveTypeCB->findData((int)curveRef.type)); connect(curveTypeCB, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int))); } @@ -204,7 +207,6 @@ void CurveReferenceUIManager::init(QObject * parent) } if (curveValueCB) { - funcItemModel = new RawItemFilteredModel(new CurveRefFuncItemModel(parent), parent); curveValueCB->setSizeAdjustPolicy(QComboBox::AdjustToContents); curveValueCB->setMaxVisibleItems(10); connect(curveValueCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valueCBChanged())); @@ -222,12 +224,12 @@ void CurveReferenceUIManager::update() lock = true; int widgetsMask = 0; - if (curve.type == CurveReference::CURVE_REF_DIFF || curve.type == CurveReference::CURVE_REF_EXPO) { + if (curveRef.type == CurveReference::CURVE_REF_DIFF || curveRef.type == CurveReference::CURVE_REF_EXPO) { if (hasCapabilityGvars) widgetsMask |= CURVE_REF_UI_GVAR_SHOW; - if (curve.isValueNumber()) { + if (curveRef.isValueNumber()) { curveGVarCB->setChecked(false); - curveValueSB->setValue(curve.value); + curveValueSB->setValue(curveRef.value); widgetsMask |= CURVE_REF_UI_VALUE_SHOW; } else { @@ -240,7 +242,7 @@ void CurveReferenceUIManager::update() } if(curveTypeCB) { - curveTypeCB->setCurrentIndex(curveTypeCB->findData(curve.type)); + curveTypeCB->setCurrentIndex(curveTypeCB->findData(curveRef.type)); curveTypeCB->show(); } if(curveGVarCB) @@ -248,7 +250,7 @@ void CurveReferenceUIManager::update() if(curveValueSB) curveValueSB->setVisible(widgetsMask & CURVE_REF_UI_VALUE_SHOW); if(curveValueCB) { - if (curve.isValueReference()) + if (curveRef.isValueReference()) populateValueCB(curveValueCB); curveValueCB->setVisible(widgetsMask & CURVE_REF_UI_REF_SHOW); } @@ -259,7 +261,7 @@ void CurveReferenceUIManager::update() void CurveReferenceUIManager::gvarCBChanged(int state) { if (!lock) { - curve.value = CurveReference::getDefaultValue(curve.type, state); + curveRef.value = CurveReference::getDefaultValue(curveRef.type, state); update(); } } @@ -268,7 +270,7 @@ void CurveReferenceUIManager::typeChanged(int value) { if (!lock) { CurveReference::CurveRefType type = (CurveReference::CurveRefType)curveTypeCB->itemData(curveTypeCB->currentIndex()).toInt(); - curve = CurveReference(type, CurveReference::getDefaultValue(type)); + curveRef = CurveReference(type, CurveReference::getDefaultValue(type)); update(); } } @@ -276,7 +278,7 @@ void CurveReferenceUIManager::typeChanged(int value) void CurveReferenceUIManager::valueSBChanged() { if (!lock) { - curve.value = curveValueSB->value(); + curveRef.value = curveValueSB->value(); update(); } } @@ -284,7 +286,7 @@ void CurveReferenceUIManager::valueSBChanged() void CurveReferenceUIManager::valueCBChanged() { if (!lock) { - curve.value = curveValueCB->itemData(curveValueCB->currentIndex()).toInt(); + curveRef.value = curveValueCB->itemData(curveValueCB->currentIndex()).toInt(); update(); } } @@ -292,7 +294,7 @@ void CurveReferenceUIManager::valueCBChanged() void CurveReferenceUIManager::populateValueCB(QComboBox * cb) { if (cb) { - switch (curve.type) { + switch (curveRef.type) { case CurveReference::CURVE_REF_DIFF: case CurveReference::CURVE_REF_EXPO: cb->setModel(gvarItemModel); @@ -307,6 +309,6 @@ void CurveReferenceUIManager::populateValueCB(QComboBox * cb) break; } - cb->setCurrentIndex(cb->findData(curve.value)); + cb->setCurrentIndex(cb->findData(curveRef.value)); } } diff --git a/companion/src/firmwares/curvereference.h b/companion/src/firmwares/curvereference.h index 119aaf597..3d61e3cb5 100644 --- a/companion/src/firmwares/curvereference.h +++ b/companion/src/firmwares/curvereference.h @@ -29,7 +29,7 @@ class ModelData; -class RawItemFilteredModel; +class FilteredItemModel; class CurveReference { @@ -90,10 +90,14 @@ class CurveReferenceUIManager : public QObject { public: CurveReferenceUIManager(QComboBox *curveValueCB, CurveReference & curve, const ModelData & model, - RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel, QObject * parent = nullptr); + FilteredItemModel * curveItemModel, FilteredItemModel * gvarItemModel, FilteredItemModel * typeItemModel, + FilteredItemModel * funcItemModel, QObject * parent = nullptr); + CurveReferenceUIManager(QComboBox *curveTypeCB, QCheckBox *curveGVarCB, QSpinBox *curveValueSB, QComboBox *curveValueCB, - CurveReference & curve, const ModelData & model, RawItemFilteredModel * curveItemModel, - RawItemFilteredModel * gvarItemModel, QObject * parent = nullptr); + CurveReference & curve, const ModelData & model, FilteredItemModel * curveItemModel, + FilteredItemModel * gvarItemModel, FilteredItemModel * typeItemModel, + FilteredItemModel * funcItemModel, QObject * parent = nullptr); + virtual ~CurveReferenceUIManager(); protected slots: @@ -103,24 +107,22 @@ class CurveReferenceUIManager : public QObject { void valueCBChanged(); void update(); - protected: + private: QComboBox *curveTypeCB; QCheckBox *curveGVarCB; QSpinBox *curveValueSB; QComboBox *curveValueCB; - CurveReference & curve; + CurveReference & curveRef; const ModelData & model; bool lock; bool hasCapabilityGvars; - RawItemFilteredModel * curveItemModel; - RawItemFilteredModel * gvarItemModel; - RawItemFilteredModel * typeItemModel; - RawItemFilteredModel * funcItemModel; + FilteredItemModel * curveItemModel; + FilteredItemModel * gvarItemModel; + FilteredItemModel * typeItemModel; + FilteredItemModel * funcItemModel; - void populateValueCB(QComboBox * cb); - - private: void init(QObject * parent); + void populateValueCB(QComboBox * cb); }; #endif // CURVEREFERENCE_H diff --git a/companion/src/generaledit/generaledit.cpp b/companion/src/generaledit/generaledit.cpp index bff9d3f83..c2a38b61d 100644 --- a/companion/src/generaledit/generaledit.cpp +++ b/companion/src/generaledit/generaledit.cpp @@ -56,10 +56,14 @@ GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * fir } } - commonItemModels = new CommonItemModels(&generalSettings, nullptr, this); + sharedItemModels = new ItemModelsFactory(&generalSettings, nullptr); + sharedItemModels->addItemModel(AbstractItemModel::RawSourceId); + sharedItemModels->addItemModel(AbstractItemModel::RawSwitchId); + sharedItemModels->addItemModel(AbstractItemModel::CustomFuncActionId); + sharedItemModels->addItemModel(AbstractItemModel::CustomFuncResetParamId); addTab(new GeneralSetupPanel(this, generalSettings, firmware), tr("Setup")); - addTab(new CustomFunctionsPanel(this, nullptr, generalSettings, firmware, commonItemModels), tr("Global Functions")); + addTab(new CustomFunctionsPanel(this, nullptr, generalSettings, firmware, sharedItemModels), tr("Global Functions")); addTab(new TrainerPanel(this, generalSettings, firmware), tr("Trainer")); addTab(new HardwarePanel(this, generalSettings, firmware), tr("Hardware")); addTab(new CalibrationPanel(this, generalSettings, firmware), tr("Calibration")); @@ -70,6 +74,7 @@ GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * fir GeneralEdit::~GeneralEdit() { delete ui; + delete sharedItemModels; } void GeneralEdit::closeEvent(QCloseEvent *event) diff --git a/companion/src/generaledit/generaledit.h b/companion/src/generaledit/generaledit.h index 3f73a9e01..b4a2ea3cc 100644 --- a/companion/src/generaledit/generaledit.h +++ b/companion/src/generaledit/generaledit.h @@ -25,7 +25,7 @@ #include "eeprominterface.h" #include "genericpanel.h" -class CommonItemModels; +class ItemModelsFactory; namespace Ui { class GeneralEdit; @@ -73,7 +73,7 @@ class GeneralEdit : public QDialog QVector panels; void addTab(GenericPanel *panel, QString text); void closeEvent(QCloseEvent *event); - CommonItemModels *commonItemModels; + ItemModelsFactory *sharedItemModels; }; #endif // _GENERALEDIT_H_ diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index d2818368e..cd83675dc 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -96,16 +96,14 @@ void LimitsGroup::updateMinMax(int max) } } } -ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels) { chnCapability = firmware->getCapability(Outputs); int channelNameMaxLen = firmware->getCapability(ChannelsName); - curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), RawItemFilteredModel::AllFilter, this); - connect(curveFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &ChannelsPanel::onModelDataAboutToBeUpdated); - connect(curveFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &ChannelsPanel::onModelDataUpdateComplete); + FILTEREDITEMMODELNOFLAGS(curveFilteredModel, ChannelsPanel, CurveId) QStringList headerLabels; headerLabels << "#"; @@ -214,6 +212,7 @@ ChannelsPanel::~ChannelsPanel() delete centerSB[i]; delete symlimitsChk[i]; } + delete curveFilteredModel; } void ChannelsPanel::symlimitsEdited() @@ -474,12 +473,12 @@ void ChannelsPanel::swapData(int idx1, int idx2) } } -void ChannelsPanel::onModelDataAboutToBeUpdated() +void ChannelsPanel::onItemModelAboutToBeUpdated() { lock = true; } -void ChannelsPanel::onModelDataUpdateComplete() +void ChannelsPanel::onItemModelUpdateComplete() { update(); lock = false; @@ -487,5 +486,5 @@ void ChannelsPanel::onModelDataUpdateComplete() void ChannelsPanel::updateItemModels() { - commonItemModels->update(CommonItemModels::RMO_CHANNELS); + sharedItemModels->update(AbstractItemModel::ChannelsUpdated); } diff --git a/companion/src/modeledit/channels.h b/companion/src/modeledit/channels.h index a481feec2..7e2d3f3fa 100644 --- a/companion/src/modeledit/channels.h +++ b/companion/src/modeledit/channels.h @@ -26,8 +26,8 @@ #include -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; constexpr char MIMETYPE_CHANNEL[] = "application/x-companion-channel"; @@ -57,8 +57,8 @@ class ChannelsPanel : public ModelPanel Q_OBJECT public: - ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); - ~ChannelsPanel(); + ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + virtual ~ChannelsPanel(); public slots: void refreshExtendedLimits(); @@ -81,8 +81,8 @@ class ChannelsPanel : public ModelPanel void cmClear(bool prompt = true); void cmClearAll(); void onCustomContextMenuRequested(QPoint pos); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: bool hasClipboardData(QByteArray * data = nullptr) const; @@ -100,8 +100,8 @@ class ChannelsPanel : public ModelPanel QCheckBox *symlimitsChk[CPN_MAX_CHNOUT]; int selectedIndex; int chnCapability; - CommonItemModels * commonItemModels; - RawItemFilteredModel *curveFilteredModel; + ItemModelsFactory *sharedItemModels; + FilteredItemModel *curveFilteredModel; void updateItemModels(); }; diff --git a/companion/src/modeledit/curves.cpp b/companion/src/modeledit/curves.cpp index 9e60f30c3..34cda04f9 100644 --- a/companion/src/modeledit/curves.cpp +++ b/companion/src/modeledit/curves.cpp @@ -109,11 +109,12 @@ float curveSymmetricalX(float x, float coeff, float yMin, float yMid, float yMax return y; } -CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Curves), currentCurve(0), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels) { ui->setupUi(this); @@ -812,7 +813,7 @@ void CurvesPanel::swapData(int idx1, int idx2) void CurvesPanel::updateItemModels() { - commonItemModels->update(CommonItemModels::RMO_CURVES); + sharedItemModels->update(AbstractItemModel::CurvesUpdated); } CustomScene::CustomScene(QGraphicsView * view) : diff --git a/companion/src/modeledit/curves.h b/companion/src/modeledit/curves.h index 8df3a8528..46ac63a01 100644 --- a/companion/src/modeledit/curves.h +++ b/companion/src/modeledit/curves.h @@ -26,7 +26,7 @@ #include #include -class CommonItemModels; +class ItemModelsFactory; constexpr char MIMETYPE_CURVE[] = "application/x-companion-curve"; @@ -61,7 +61,7 @@ class CurvesPanel : public ModelPanel Q_OBJECT public: - CurvesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + CurvesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); virtual ~CurvesPanel(); virtual void update(); @@ -121,7 +121,7 @@ class CurvesPanel : public ModelPanel bool moveDownAllowed() const; bool moveUpAllowed() const; void swapData(int idx1, int idx2); - CommonItemModels * commonItemModels; + ItemModelsFactory * sharedItemModels; void updateItemModels(); }; diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index c3ee895b7..94949064b 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -66,10 +66,10 @@ void RepeatComboBox::update() setCurrentIndex(value); } -CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels): GenericPanel(parent, model, generalSettings, firmware), functions(model ? model->customFn : generalSettings.customFn), - commonItemModels(commonItemModels), mediaPlayerCurrent(-1), mediaPlayer(nullptr), modelsUpdateCnt(0) @@ -77,21 +77,10 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, lock = true; fswCapability = model ? firmware->getCapability(CustomFunctions) : firmware->getCapability(GlobalFunctions); - rawSwitchFilteredModel = new RawItemFilteredModel(commonItemModels->rawSwitchItemModel(), model ? RawSwitch::SpecialFunctionsContext : RawSwitch::GlobalFunctionsContext, this); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &CustomFunctionsPanel::onModelDataAboutToBeUpdated); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &CustomFunctionsPanel::onModelDataUpdateComplete); - - rawSourceAllModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), this); - connect(rawSourceAllModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &CustomFunctionsPanel::onModelDataAboutToBeUpdated); - connect(rawSourceAllModel, &RawItemFilteredModel::dataUpdateComplete, this, &CustomFunctionsPanel::onModelDataUpdateComplete); - - rawSourceInputsModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), RawSource::InputSourceGroups, this); - connect(rawSourceInputsModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &CustomFunctionsPanel::onModelDataAboutToBeUpdated); - connect(rawSourceInputsModel, &RawItemFilteredModel::dataUpdateComplete, this, &CustomFunctionsPanel::onModelDataUpdateComplete); - - rawSourceGVarsModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), RawSource::GVarsGroup, this); - connect(rawSourceGVarsModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &CustomFunctionsPanel::onModelDataAboutToBeUpdated); - connect(rawSourceGVarsModel, &RawItemFilteredModel::dataUpdateComplete, this, &CustomFunctionsPanel::onModelDataUpdateComplete); + FILTEREDITEMMODEL(rawSwitchFilteredModel, CustomFunctionsPanel, RawSwitchId, model ? RawSwitch::SpecialFunctionsContext : RawSwitch::GlobalFunctionsContext) + FILTEREDITEMMODELNOFLAGS(rawSourceAllModel, CustomFunctionsPanel, RawSourceId) + FILTEREDITEMMODEL(rawSourceInputsModel, CustomFunctionsPanel, RawSourceId, RawSource::InputSourceGroups) + FILTEREDITEMMODEL(rawSourceGVarsModel, CustomFunctionsPanel, RawSourceId, RawSource::GVarsGroup) if (!firmware->getCapability(VoicesAsNumbers)) { tracksSet = getFilesSet(getSoundsPath(generalSettings), QStringList() << "*.wav" << "*.WAV", firmware->getCapability(VoicesMaxLength)); @@ -253,6 +242,10 @@ CustomFunctionsPanel::~CustomFunctionsPanel() { if (mediaPlayer) stopSound(mediaPlayerCurrent); + delete rawSwitchFilteredModel; + delete rawSourceAllModel; + delete rawSourceInputsModel; + delete rawSourceGVarsModel; } void CustomFunctionsPanel::onMediaPlayerStateChanged(QMediaPlayer::State state) @@ -827,13 +820,13 @@ bool CustomFunctionsPanel::moveUpAllowed() const return selectedIndex > 0; } -void CustomFunctionsPanel::onModelDataAboutToBeUpdated() +void CustomFunctionsPanel::onItemModelAboutToBeUpdated() { lock = true; modelsUpdateCnt++; } -void CustomFunctionsPanel::onModelDataUpdateComplete() +void CustomFunctionsPanel::onItemModelUpdateComplete() { modelsUpdateCnt--; if (modelsUpdateCnt < 1) { diff --git a/companion/src/modeledit/customfunctions.h b/companion/src/modeledit/customfunctions.h index 9b3e3d238..ef8e68b5f 100644 --- a/companion/src/modeledit/customfunctions.h +++ b/companion/src/modeledit/customfunctions.h @@ -26,8 +26,8 @@ #include -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; class TimerEdit; constexpr char MIMETYPE_CUSTOM_FUNCTION[] = "application/x-companion-custom-function"; @@ -55,8 +55,8 @@ class CustomFunctionsPanel : public GenericPanel Q_OBJECT public: - CustomFunctionsPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); - ~CustomFunctionsPanel(); + CustomFunctionsPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + virtual ~CustomFunctionsPanel(); virtual void update(); @@ -83,8 +83,8 @@ class CustomFunctionsPanel : public GenericPanel void cmInsert(); void cmClear(bool prompt = true); void cmClearAll(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: void populateFuncCB(QComboBox *b, unsigned int value); @@ -96,11 +96,10 @@ class CustomFunctionsPanel : public GenericPanel bool moveUpAllowed() const; void swapData(int idx1, int idx2); void resetCBsAndRefresh(int idx); - CommonItemModels * commonItemModels; - RawItemFilteredModel * rawSwitchFilteredModel; - RawItemFilteredModel * rawSourceAllModel; - RawItemFilteredModel * rawSourceInputsModel; - RawItemFilteredModel * rawSourceGVarsModel; + FilteredItemModel * rawSwitchFilteredModel; + FilteredItemModel * rawSourceAllModel; + FilteredItemModel * rawSourceInputsModel; + FilteredItemModel * rawSourceGVarsModel; QSet tracksSet; QSet scriptsSet; diff --git a/companion/src/modeledit/expodialog.cpp b/companion/src/modeledit/expodialog.cpp index 217f787d2..1bf22c68e 100644 --- a/companion/src/modeledit/expodialog.cpp +++ b/companion/src/modeledit/expodialog.cpp @@ -24,8 +24,9 @@ #include "helpers.h" ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, GeneralSettings & generalSettings, - Firmware * firmware, QString & inputName, RawItemFilteredModel * rawSourceModel, - RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel) : + Firmware * firmware, QString & inputName, FilteredItemModel * rawSourceFilteredModel, + FilteredItemModel * rawSwitchFilteredModel, FilteredItemModel * curveFilteredModel, + FilteredItemModel * gvarFilteredModel, FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel) : QDialog(parent), ui(new Ui::ExpoDialog), model(model), @@ -50,10 +51,10 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, -100, 100); gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, ed->offset, model, 0, -100, 100); - curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, curveItemModel, - gvarItemModel, this); + curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, + curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel, this); - ui->switchesCB->setModel(rawSwitchModel); + ui->switchesCB->setModel(rawSwitchFilteredModel); ui->switchesCB->setCurrentIndex(ui->switchesCB->findData(ed->swtch.toValue())); ui->sideCB->setCurrentIndex(ed->mode - 1); @@ -84,7 +85,7 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G if (firmware->getCapability(VirtualInputs)) { ui->inputName->setMaxLength(firmware->getCapability(InputsLength)); - ui->sourceCB->setModel(rawSourceModel); + ui->sourceCB->setModel(rawSourceFilteredModel); ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(ed->srcRaw.toValue())); ui->inputName->setValidator(new QRegExpValidator(rx, this)); ui->inputName->setText(inputName); diff --git a/companion/src/modeledit/expodialog.h b/companion/src/modeledit/expodialog.h index 0b17900df..427fe9572 100644 --- a/companion/src/modeledit/expodialog.h +++ b/companion/src/modeledit/expodialog.h @@ -26,7 +26,7 @@ #include "modelprinter.h" class GVarGroup; -class RawItemFilteredModel; +class FilteredItemModel; class CurveReferenceUIManager; namespace Ui { @@ -37,9 +37,10 @@ class ExpoDialog : public QDialog { Q_OBJECT public: ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expodata, GeneralSettings & generalSettings, - Firmware * firmware, QString & inputName, RawItemFilteredModel * rawSourceItemModel, - RawItemFilteredModel * rawSwitchItemModel, RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel); - ~ExpoDialog(); + Firmware * firmware, QString & inputName, FilteredItemModel * rawSourceFilteredModel, + FilteredItemModel * rawSwitchFilteredModel, FilteredItemModel * curveFilteredModel, FilteredItemModel * gvarFilteredModel, + FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel); + virtual ~ExpoDialog(); protected: void updateScale(); diff --git a/companion/src/modeledit/flightmodes.cpp b/companion/src/modeledit/flightmodes.cpp index 32188feea..a60c5f09b 100644 --- a/companion/src/modeledit/flightmodes.cpp +++ b/companion/src/modeledit/flightmodes.cpp @@ -24,15 +24,16 @@ #include "helpers.h" #include "customdebug.h" -FlightModePanel::FlightModePanel(QWidget * parent, ModelData & model, int phaseIdx, GeneralSettings & generalSettings, Firmware * firmware, RawItemFilteredModel * rawSwitchFilteredModel): +FlightModePanel::FlightModePanel(QWidget * parent, ModelData & model, int phaseIdx, GeneralSettings & generalSettings, Firmware * firmware, + FilteredItemModel * rawSwitchFilteredModel): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::FlightMode), phaseIdx(phaseIdx), phase(model.flightModeData[phaseIdx]) { ui->setupUi(this); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &FlightModePanel::onModelDataAboutToBeUpdated); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &FlightModePanel::onModelDataUpdateComplete); + connect(rawSwitchFilteredModel, &FilteredItemModel::aboutToBeUpdated, this, &FlightModePanel::onItemModelAboutToBeUpdated); + connect(rawSwitchFilteredModel, &FilteredItemModel::updateComplete, this, &FlightModePanel::onItemModelUpdateComplete); ui->labelName->setContextMenuPolicy(Qt::CustomContextMenu); ui->labelName->setToolTip(tr("Popup menu available")); @@ -1361,12 +1362,12 @@ void FlightModePanel::gvSwapData(int idx1, int idx2) } } -void FlightModePanel::onModelDataAboutToBeUpdated() +void FlightModePanel::onItemModelAboutToBeUpdated() { lock = true; } -void FlightModePanel::onModelDataUpdateComplete() +void FlightModePanel::onItemModelUpdateComplete() { update(); lock = false; @@ -1374,11 +1375,13 @@ void FlightModePanel::onModelDataUpdateComplete() /**********************************************************/ -FlightModesPanel::FlightModesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +FlightModesPanel::FlightModesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels) { - rawSwitchFilteredModel = new RawItemFilteredModel(commonItemModels->rawSwitchItemModel(), RawSwitch::MixesContext, this); + FILTEREDITEMMODEL(rawSwitchFilteredModel, FlightModesPanel, RawSwitchId, RawSwitch::MixesContext) + modesCount = firmware->getCapability(FlightModes); QGridLayout * gridLayout = new QGridLayout(this); @@ -1403,6 +1406,7 @@ FlightModesPanel::FlightModesPanel(QWidget * parent, ModelData & model, GeneralS FlightModesPanel::~FlightModesPanel() { + delete rawSwitchFilteredModel; } QString FlightModesPanel::getTabName(int index) @@ -1431,10 +1435,18 @@ void FlightModesPanel::update() emit updated(); } +void FlightModesPanel::onItemModelAboutToBeUpdated() +{ +} + +void FlightModesPanel::onItemModelUpdateComplete() +{ +} + void FlightModesPanel::updateItemModels() { - commonItemModels->update(CommonItemModels::RMO_FLIGHT_MODES); - commonItemModels->update(CommonItemModels::RMO_GLOBAL_VARIABLES); + sharedItemModels->update(AbstractItemModel::FlightModesUpdated); + sharedItemModels->update(AbstractItemModel::GVarsUpdated); } void FlightModesPanel::onTabIndexChanged(int index) diff --git a/companion/src/modeledit/flightmodes.h b/companion/src/modeledit/flightmodes.h index ca3619c6c..e74dec8bb 100644 --- a/companion/src/modeledit/flightmodes.h +++ b/companion/src/modeledit/flightmodes.h @@ -24,8 +24,8 @@ #include "modeledit.h" #include "eeprominterface.h" -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; constexpr char MIMETYPE_FLIGHTMODE[] = "application/x-companion-flightmode"; constexpr char MIMETYPE_GVAR_PARAMS[] = "application/x-companion-gvar-params"; @@ -41,7 +41,8 @@ class FlightModePanel : public ModelPanel Q_OBJECT public: - FlightModePanel(QWidget *parent, ModelData &model, int modeIdx, GeneralSettings & generalSettings, Firmware * firmware, RawItemFilteredModel * rawSwitchFilteredModel); + FlightModePanel(QWidget *parent, ModelData &model, int modeIdx, GeneralSettings & generalSettings, Firmware * firmware, + FilteredItemModel * rawSwitchFilteredModel); virtual ~FlightModePanel(); virtual void update(); @@ -90,8 +91,8 @@ class FlightModePanel : public ModelPanel void gvCmPaste(); void gvCmMoveDown(); void gvCmMoveUp(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: Ui::FlightMode *ui; @@ -145,7 +146,7 @@ class FlightModesPanel : public ModelPanel Q_OBJECT public: - FlightModesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + FlightModesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); virtual ~FlightModesPanel(); public slots: @@ -157,14 +158,16 @@ class FlightModesPanel : public ModelPanel private slots: void onPhaseNameChanged(); void onTabIndexChanged(int index); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: QString getTabName(int index); int modesCount; QTabWidget *tabWidget; - CommonItemModels *commonItemModels; - RawItemFilteredModel *rawSwitchFilteredModel; + ItemModelsFactory *sharedItemModels; + FilteredItemModel *rawSwitchFilteredModel; QVector panels; void updateItemModels(); }; diff --git a/companion/src/modeledit/heli.cpp b/companion/src/modeledit/heli.cpp index 1b8607799..a1142468e 100644 --- a/companion/src/modeledit/heli.cpp +++ b/companion/src/modeledit/heli.cpp @@ -23,16 +23,13 @@ #include "helpers.h" #include "rawitemfilteredmodel.h" -HeliPanel::HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +HeliPanel::HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), - ui(new Ui::Heli), - commonItemModels(commonItemModels) + ui(new Ui::Heli) { ui->setupUi(this); - rawSourceFilteredModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), RawSource::InputSourceGroups, this); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &HeliPanel::onModelDataAboutToBeUpdated); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &HeliPanel::onModelDataUpdateComplete); + FILTEREDITEMMODEL(rawSourceFilteredModel, HeliPanel, RawSourceId, RawSource::InputSourceGroups) connect(ui->swashType, SIGNAL(currentIndexChanged(int)), this, SLOT(edited())); connect(ui->swashRingVal, SIGNAL(editingFinished()), this, SLOT(edited())); @@ -71,6 +68,7 @@ HeliPanel::HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & gener HeliPanel::~HeliPanel() { delete ui; + delete rawSourceFilteredModel; } void HeliPanel::update() @@ -118,12 +116,12 @@ void HeliPanel::edited() } } -void HeliPanel::onModelDataAboutToBeUpdated() +void HeliPanel::onItemModelAboutToBeUpdated() { lock = true; } -void HeliPanel::onModelDataUpdateComplete() +void HeliPanel::onItemModelUpdateComplete() { update(); lock = false; diff --git a/companion/src/modeledit/heli.h b/companion/src/modeledit/heli.h index ee9a3ac36..2238a67f1 100644 --- a/companion/src/modeledit/heli.h +++ b/companion/src/modeledit/heli.h @@ -23,8 +23,8 @@ #include "modeledit.h" -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; namespace Ui { class Heli; @@ -35,19 +35,18 @@ class HeliPanel : public ModelPanel Q_OBJECT public: - HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); - ~HeliPanel(); + HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + virtual ~HeliPanel(); void update(); private slots: void edited(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: Ui::Heli *ui; - CommonItemModels * commonItemModels; - RawItemFilteredModel * rawSourceFilteredModel; + FilteredItemModel * rawSourceFilteredModel; }; #endif // _HELI_H_ diff --git a/companion/src/modeledit/inputs.cpp b/companion/src/modeledit/inputs.cpp index 5e50de71c..ae25ae451 100644 --- a/companion/src/modeledit/inputs.cpp +++ b/companion/src/modeledit/inputs.cpp @@ -23,28 +23,18 @@ #include "helpers.h" #include "rawitemfilteredmodel.h" -InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), expoInserted(false), modelPrinter(firmware, generalSettings, model), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels) { - rawSourceFilteredModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), (RawSource::InputSourceGroups & ~ RawSource::NoneGroup & ~RawSource::InputsGroup), this); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); - - rawSwitchFilteredModel = new RawItemFilteredModel(commonItemModels->rawSwitchItemModel(), RawSwitch::MixesContext, this); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); - - const int filter = firmware->getCapability(HasInputDiff) ? RawItemFilteredModel::AllFilter : RawItemFilteredModel::PositiveFilter; - curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), filter, this); - connect(curveFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); - connect(curveFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); - - gvarFilteredModel = new RawItemFilteredModel(commonItemModels->gvarItemModel(), RawItemFilteredModel::AllFilter, this); - connect(gvarFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); - connect(gvarFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); + FILTEREDITEMMODEL(rawSourceFilteredModel, InputsPanel, RawSourceId,(RawSource::InputSourceGroups & ~ RawSource::NoneGroup & ~RawSource::InputsGroup)) + FILTEREDITEMMODEL(rawSwitchFilteredModel, InputsPanel, RawSwitchId, RawSwitch::MixesContext) + FILTEREDITEMMODEL(curveFilteredModel, InputsPanel, CurveId, firmware->getCapability(HasInputDiff) ? 0 : FilteredItemModel::PositiveFilter) + FILTEREDITEMMODELNOFLAGS(gvarFilteredModel, InputsPanel, GVarRefId) + FILTEREDITEMMODELNOFLAGS(crTypeFilteredModel, InputsPanel, CurveRefTypeId) + FILTEREDITEMMODELNOFLAGS(crFuncFilteredModel, InputsPanel, CurveRefFuncId) inputsCount = firmware->getCapability(VirtualInputs); if (inputsCount == 0) @@ -84,6 +74,12 @@ InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & g InputsPanel::~InputsPanel() { + delete rawSourceFilteredModel; + delete rawSwitchFilteredModel; + delete curveFilteredModel; + delete gvarFilteredModel; + delete crTypeFilteredModel; + delete crFuncFilteredModel; } void InputsPanel::update() @@ -200,7 +196,7 @@ void InputsPanel::gm_openExpo(int index) inputName = model->inputNames[ed.chn]; ExpoDialog *dlg = new ExpoDialog(this, *model, &ed, generalSettings, firmware, inputName, rawSourceFilteredModel, rawSwitchFilteredModel, - curveFilteredModel, gvarFilteredModel); + curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel); if (dlg->exec()) { model->expoData[index] = ed; if (firmware->getCapability(VirtualInputs)) @@ -736,12 +732,12 @@ int InputsPanel::getInputIndexFromSelected() return idx; } -void InputsPanel::onModelDataAboutToBeUpdated() +void InputsPanel::onItemModelAboutToBeUpdated() { lock = true; } -void InputsPanel::onModelDataUpdateComplete() +void InputsPanel::onItemModelUpdateComplete() { update(); lock = false; @@ -749,5 +745,5 @@ void InputsPanel::onModelDataUpdateComplete() void InputsPanel::updateItemModels() { - commonItemModels->update(CommonItemModels::RMO_INPUTS); + sharedItemModels->update(AbstractItemModel::InputsUpdated); } diff --git a/companion/src/modeledit/inputs.h b/companion/src/modeledit/inputs.h index 3bbfceb7b..8b2c6bcef 100644 --- a/companion/src/modeledit/inputs.h +++ b/companion/src/modeledit/inputs.h @@ -25,8 +25,8 @@ #include "mixerslistwidget.h" #include "modelprinter.h" -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; constexpr char MIMETYPE_EXPO[] = "application/x-companion-expo"; @@ -35,7 +35,7 @@ class InputsPanel : public ModelPanel Q_OBJECT public: - InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); virtual ~InputsPanel(); virtual void update(); @@ -62,8 +62,8 @@ class InputsPanel : public ModelPanel void cmInputInsert(); void cmInputMoveDown(); void cmInputMoveUp(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: bool expoInserted; @@ -72,11 +72,13 @@ class InputsPanel : public ModelPanel ModelPrinter modelPrinter; int selectedIdx; int inputIdx; - CommonItemModels * commonItemModels; - RawItemFilteredModel *rawSourceFilteredModel; - RawItemFilteredModel *rawSwitchFilteredModel; - RawItemFilteredModel *curveFilteredModel; - RawItemFilteredModel *gvarFilteredModel; + ItemModelsFactory *sharedItemModels; + FilteredItemModel *rawSourceFilteredModel; + FilteredItemModel *rawSwitchFilteredModel; + FilteredItemModel *curveFilteredModel; + FilteredItemModel *gvarFilteredModel; + FilteredItemModel *crTypeFilteredModel; + FilteredItemModel *crFuncFilteredModel; int getExpoIndex(unsigned int dch); bool gm_insertExpo(int idx); diff --git a/companion/src/modeledit/logicalswitches.cpp b/companion/src/modeledit/logicalswitches.cpp index cacd5c6fd..dc4755f53 100644 --- a/companion/src/modeledit/logicalswitches.cpp +++ b/companion/src/modeledit/logicalswitches.cpp @@ -24,20 +24,16 @@ #include -LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), - commonItemModels(commonItemModels), + sharedItemModels(sharedItemModels), selectedIndex(0), modelsUpdateCnt(0) { - rawSwitchFilteredModel = new RawItemFilteredModel(commonItemModels->rawSwitchItemModel(), RawSwitch::LogicalSwitchesContext, this); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &LogicalSwitchesPanel::onModelDataAboutToBeUpdated); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &LogicalSwitchesPanel::onModelDataUpdateComplete); - + FILTEREDITEMMODEL(rawSwitchFilteredModel, LogicalSwitchesPanel, RawSwitchId, RawSwitch::LogicalSwitchesContext) const int srcGroups = firmware->getCapability(GvarsInCS) ? 0 : (RawSource::AllSourceGroups & ~RawSource::GVarsGroup); - rawSourceFilteredModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), srcGroups, this); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &LogicalSwitchesPanel::onModelDataAboutToBeUpdated); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &LogicalSwitchesPanel::onModelDataUpdateComplete); + FILTEREDITEMMODEL(rawSourceFilteredModel, LogicalSwitchesPanel, RawSourceId, srcGroups) lsCapability = firmware->getCapability(LogicalSwitches); lsCapabilityExt = firmware->getCapability(LogicalSwitchesExt); @@ -161,6 +157,8 @@ LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, LogicalSwitchesPanel::~LogicalSwitchesPanel() { + delete rawSourceFilteredModel; + delete rawSwitchFilteredModel; } void LogicalSwitchesPanel::onFunctionChanged() @@ -644,16 +642,16 @@ void LogicalSwitchesPanel::swapData(int idx1, int idx2) void LogicalSwitchesPanel::updateItemModels() { lock = true; - commonItemModels->update(CommonItemModels::RMO_LOGICAL_SWITCHES); + sharedItemModels->update(AbstractItemModel::LogicalSwitchesUpdated); } -void LogicalSwitchesPanel::onModelDataAboutToBeUpdated() +void LogicalSwitchesPanel::onItemModelAboutToBeUpdated() { lock = true; modelsUpdateCnt++; } -void LogicalSwitchesPanel::onModelDataUpdateComplete() +void LogicalSwitchesPanel::onItemModelUpdateComplete() { modelsUpdateCnt--; if (modelsUpdateCnt < 1) { diff --git a/companion/src/modeledit/logicalswitches.h b/companion/src/modeledit/logicalswitches.h index 87a360408..05b683b49 100644 --- a/companion/src/modeledit/logicalswitches.h +++ b/companion/src/modeledit/logicalswitches.h @@ -24,8 +24,8 @@ #include "modeledit.h" #include "radiodata.h" -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; class TimerEdit; constexpr char MIMETYPE_LOGICAL_SWITCH[] = "application/x-companion-logical-switch"; @@ -35,7 +35,7 @@ class LogicalSwitchesPanel : public ModelPanel Q_OBJECT public: - LogicalSwitchesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + LogicalSwitchesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); virtual ~LogicalSwitchesPanel(); virtual void update(); @@ -60,8 +60,8 @@ class LogicalSwitchesPanel : public ModelPanel void cmInsert(); void cmClear(bool prompt = true); void cmClearAll(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: QComboBox * cbFunction[CPN_MAX_LOGICAL_SWITCHES]; @@ -74,9 +74,9 @@ class LogicalSwitchesPanel : public ModelPanel QDoubleSpinBox * dsbDelay[CPN_MAX_LOGICAL_SWITCHES]; QComboBox * cbSource1[CPN_MAX_LOGICAL_SWITCHES]; QComboBox * cbSource2[CPN_MAX_LOGICAL_SWITCHES]; - CommonItemModels * commonItemModels; - RawItemFilteredModel * rawSwitchFilteredModel; - RawItemFilteredModel * rawSourceFilteredModel; + ItemModelsFactory * sharedItemModels; + FilteredItemModel * rawSwitchFilteredModel; + FilteredItemModel * rawSourceFilteredModel; int selectedIndex; void populateFunctionCB(QComboBox *b); void updateTimerParam(QDoubleSpinBox *sb, int timer, double minimum=0); diff --git a/companion/src/modeledit/mixerdialog.cpp b/companion/src/modeledit/mixerdialog.cpp index 102d2d2a9..819649698 100644 --- a/companion/src/modeledit/mixerdialog.cpp +++ b/companion/src/modeledit/mixerdialog.cpp @@ -25,8 +25,9 @@ #include "helpers.h" MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, GeneralSettings & generalSettings, Firmware * firmware, - RawItemFilteredModel * rawSourceModel, RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel, - RawItemFilteredModel * gvarItemModel) : + FilteredItemModel * rawSourceFilteredModel, FilteredItemModel * rawSwitchFilteredModel, + FilteredItemModel * curveFilteredModel, FilteredItemModel * gvarFilteredModel, + FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel) : QDialog(parent), ui(new Ui::MixerDialog), model(model), @@ -46,15 +47,15 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, this->setWindowTitle(tr("DEST -> %1").arg(RawSource(SOURCE_TYPE_CH, md->destCh - 1).toString(&model, &generalSettings))); - ui->sourceCB->setModel(rawSourceModel); + ui->sourceCB->setModel(rawSourceFilteredModel); ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(md->srcRaw.toValue())); int limit = firmware->getCapability(OffsetWeight); gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, md->weight, model, 100, -limit, limit); gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, md->sOffset, model, 0, -limit, limit); - curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, md->curve, model, curveItemModel, - gvarItemModel, this); + curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, md->curve, model, + curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel, this); ui->MixDR_CB->setChecked(md->noExpo == 0); @@ -106,7 +107,7 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, } } - ui->switchesCB->setModel(rawSwitchModel); + ui->switchesCB->setModel(rawSwitchFilteredModel); ui->switchesCB->setCurrentIndex(ui->switchesCB->findData(md->swtch.toValue())); ui->warningCB->setCurrentIndex(md->mixWarn); ui->mltpxCB->setCurrentIndex(md->mltpx); diff --git a/companion/src/modeledit/mixerdialog.h b/companion/src/modeledit/mixerdialog.h index d036832d9..1beb3b220 100644 --- a/companion/src/modeledit/mixerdialog.h +++ b/companion/src/modeledit/mixerdialog.h @@ -25,7 +25,7 @@ #include "eeprominterface.h" class GVarGroup; -class RawItemFilteredModel; +class FilteredItemModel; class CurveReferenceUIManager; namespace Ui { @@ -36,8 +36,8 @@ class MixerDialog : public QDialog { Q_OBJECT public: MixerDialog(QWidget *parent, ModelData & model, MixData *mixdata, GeneralSettings & generalSettings, Firmware * firmware, - RawItemFilteredModel * rawSourceModel, RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel, - RawItemFilteredModel * gvarItemModel); + FilteredItemModel * rawSourceFilteredModel, FilteredItemModel * rawSwitchFilteredModel, FilteredItemModel * curveFilteredModel, + FilteredItemModel * gvarFilteredModel, FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel); ~MixerDialog(); protected: diff --git a/companion/src/modeledit/mixes.cpp b/companion/src/modeledit/mixes.cpp index 01b4d7f64..5bcc69d30 100644 --- a/companion/src/modeledit/mixes.cpp +++ b/companion/src/modeledit/mixes.cpp @@ -22,29 +22,19 @@ #include "helpers.h" #include "rawitemfilteredmodel.h" -MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware,CommonItemModels * commonItemModels): +MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), mixInserted(false), highlightedSource(0), modelPrinter(firmware, generalSettings, model), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels) { - rawSourceFilteredModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), ((RawSource::InputSourceGroups | RawSource::ScriptsGroup) & ~ RawSource::NoneGroup), this); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); - - rawSwitchFilteredModel = new RawItemFilteredModel(commonItemModels->rawSwitchItemModel(), RawSwitch::MixesContext, this); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); - - const int filter = firmware->getCapability(HasMixerExpo) ? RawItemFilteredModel::AllFilter : RawItemFilteredModel::PositiveFilter; - curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), filter, this); - connect(curveFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); - connect(curveFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); - - gvarFilteredModel = new RawItemFilteredModel(commonItemModels->gvarItemModel(), RawItemFilteredModel::AllFilter, this); - connect(gvarFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); - connect(gvarFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); + FILTEREDITEMMODEL(rawSourceFilteredModel, MixesPanel, RawSourceId,((RawSource::InputSourceGroups | RawSource::ScriptsGroup) & ~ RawSource::NoneGroup)) + FILTEREDITEMMODEL(rawSwitchFilteredModel, MixesPanel, RawSwitchId, RawSwitch::MixesContext) + FILTEREDITEMMODEL(curveFilteredModel, MixesPanel, CurveId, firmware->getCapability(HasMixerExpo) ? 0 : FilteredItemModel::PositiveFilter) + FILTEREDITEMMODELNOFLAGS(gvarFilteredModel, MixesPanel, GVarRefId) + FILTEREDITEMMODELNOFLAGS(crTypeFilteredModel, MixesPanel, CurveRefTypeId) + FILTEREDITEMMODELNOFLAGS(crFuncFilteredModel, MixesPanel, CurveRefFuncId) QGridLayout * mixesLayout = new QGridLayout(this); @@ -79,6 +69,12 @@ MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & gen MixesPanel::~MixesPanel() { + delete rawSourceFilteredModel; + delete rawSwitchFilteredModel; + delete curveFilteredModel; + delete gvarFilteredModel; + delete crTypeFilteredModel; + delete crFuncFilteredModel; } void MixesPanel::update() @@ -194,8 +190,8 @@ void MixesPanel::gm_openMix(int index) MixData mixd(model->mixData[index]); - MixerDialog *dlg = new MixerDialog(this, *model, &mixd, generalSettings, firmware, rawSourceFilteredModel, rawSwitchFilteredModel, - curveFilteredModel, gvarFilteredModel); + MixerDialog *dlg = new MixerDialog(this, *model, &mixd, generalSettings, firmware, rawSourceFilteredModel, rawSwitchFilteredModel, + curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel); if(dlg->exec()) { model->mixData[index] = mixd; emit modified(); @@ -551,12 +547,12 @@ void MixesPanel::clearMixes() } } -void MixesPanel::onModelDataAboutToBeUpdated() +void MixesPanel::onItemModelAboutToBeUpdated() { lock = true; } -void MixesPanel::onModelDataUpdateComplete() +void MixesPanel::onItemModelUpdateComplete() { update(); lock = false; diff --git a/companion/src/modeledit/mixes.h b/companion/src/modeledit/mixes.h index 154eda9b4..211632a2e 100644 --- a/companion/src/modeledit/mixes.h +++ b/companion/src/modeledit/mixes.h @@ -26,15 +26,15 @@ #include "mixerdialog.h" #include "modelprinter.h" -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; class MixesPanel : public ModelPanel { Q_OBJECT public: - MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); virtual ~MixesPanel(); virtual void update(); @@ -60,19 +60,21 @@ class MixesPanel : public ModelPanel void mimeMixerDropped(int index, const QMimeData *data, Qt::DropAction action); void pasteMixerMimeData(const QMimeData * mimeData, int destIdx); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: MixersListWidget * mixersListWidget; bool mixInserted; unsigned int highlightedSource; ModelPrinter modelPrinter; - CommonItemModels * commonItemModels; - RawItemFilteredModel *rawSourceFilteredModel; - RawItemFilteredModel *rawSwitchFilteredModel; - RawItemFilteredModel *curveFilteredModel; - RawItemFilteredModel *gvarFilteredModel; + ItemModelsFactory *sharedItemModels; + FilteredItemModel *rawSourceFilteredModel; + FilteredItemModel *rawSwitchFilteredModel; + FilteredItemModel *curveFilteredModel; + FilteredItemModel *gvarFilteredModel; + FilteredItemModel *crTypeFilteredModel; + FilteredItemModel *crFuncFilteredModel; int getMixerIndex(unsigned int dch); bool gm_insertMix(int idx); diff --git a/companion/src/modeledit/modeledit.cpp b/companion/src/modeledit/modeledit.cpp index 41989ba53..569b64e33 100644 --- a/companion/src/modeledit/modeledit.cpp +++ b/companion/src/modeledit/modeledit.cpp @@ -53,42 +53,53 @@ ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmw GeneralSettings &generalSettings = radioData.generalSettings; ModelData &model = radioData.models[modelId]; - commonItemModels = new CommonItemModels(&generalSettings, &model, this); + sharedItemModels = new ItemModelsFactory(&generalSettings, &model); + sharedItemModels->addItemModel(AbstractItemModel::RawSourceId); + sharedItemModels->addItemModel(AbstractItemModel::RawSwitchId); + sharedItemModels->addItemModel(AbstractItemModel::CurveId); + sharedItemModels->addItemModel(AbstractItemModel::GVarRefId); + sharedItemModels->addItemModel(AbstractItemModel::ThrSourceId); + sharedItemModels->addItemModel(AbstractItemModel::CustomFuncActionId); + sharedItemModels->addItemModel(AbstractItemModel::CustomFuncResetParamId); + sharedItemModels->addItemModel(AbstractItemModel::TeleSourceId); + sharedItemModels->addItemModel(AbstractItemModel::CurveRefTypeId); + sharedItemModels->addItemModel(AbstractItemModel::CurveRefFuncId); + s1.report("Init"); - SetupPanel * setupPanel = new SetupPanel(this, model, generalSettings, firmware, commonItemModels); + SetupPanel * setupPanel = new SetupPanel(this, model, generalSettings, firmware, sharedItemModels); addTab(setupPanel, tr("Setup")); s1.report("Setup"); if (firmware->getCapability(Heli)) { - addTab(new HeliPanel(this, model, generalSettings, firmware, commonItemModels), tr("Heli")); + addTab(new HeliPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Heli")); s1.report("Heli"); } - addTab(new FlightModesPanel(this, model, generalSettings, firmware, commonItemModels), tr("Flight Modes")); + addTab(new FlightModesPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Flight Modes")); s1.report("Flight Modes"); - addTab(new InputsPanel(this, model, generalSettings, firmware, commonItemModels), tr("Inputs")); + addTab(new InputsPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Inputs")); s1.report("Inputs"); - addTab(new MixesPanel(this, model, generalSettings, firmware, commonItemModels), tr("Mixes")); + addTab(new MixesPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Mixes")); s1.report("Mixes"); - ChannelsPanel * channelsPanel = new ChannelsPanel(this, model, generalSettings, firmware, commonItemModels); + ChannelsPanel * channelsPanel = new ChannelsPanel(this, model, generalSettings, firmware, sharedItemModels); addTab(channelsPanel, tr("Outputs")); s1.report("Outputs"); - addTab(new CurvesPanel(this, model, generalSettings, firmware, commonItemModels), tr("Curves")); + addTab(new CurvesPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Curves")); s1.report("Curves"); - addTab(new LogicalSwitchesPanel(this, model, generalSettings, firmware, commonItemModels), tr("Logical Switches")); + addTab(new LogicalSwitchesPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Logical Switches")); s1.report("Logical Switches"); - addTab(new CustomFunctionsPanel(this, &model, generalSettings, firmware, commonItemModels), tr("Special Functions")); + addTab(new CustomFunctionsPanel(this, &model, generalSettings, firmware, sharedItemModels), tr("Special Functions")); s1.report("Special Functions"); if (firmware->getCapability(Telemetry)) { - addTab(new TelemetryPanel(this, model, generalSettings, firmware, commonItemModels), tr("Telemetry")); + addTab(new TelemetryPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Telemetry")); s1.report("Telemetry"); } @@ -104,6 +115,7 @@ ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmw ModelEdit::~ModelEdit() { delete ui; + delete sharedItemModels; } void ModelEdit::closeEvent(QCloseEvent *event) diff --git a/companion/src/modeledit/modeledit.h b/companion/src/modeledit/modeledit.h index 9095101bf..99337f862 100644 --- a/companion/src/modeledit/modeledit.h +++ b/companion/src/modeledit/modeledit.h @@ -25,7 +25,7 @@ #include "genericpanel.h" class RadioData; -class CommonItemModels; +class ItemModelsFactory; namespace Ui { class ModelEdit; @@ -60,12 +60,12 @@ class ModelEdit : public QDialog private: Ui::ModelEdit *ui; int modelId; - RadioData & radioData; - Firmware * firmware; + RadioData &radioData; + Firmware *firmware; QVector panels; - CommonItemModels * commonItemModels; + ItemModelsFactory *sharedItemModels; - void addTab(GenericPanel *panel, QString text); + void addTab(GenericPanel * panel, QString text); void launchSimulation(); }; diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index e4f67178d..d3f58322e 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -31,14 +31,15 @@ #include -TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, QWidget * prevFocus, RawItemFilteredModel * rawSwitchFilteredModel): +TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, + QWidget * prevFocus, FilteredItemModel * rawSwitchFilteredModel): ModelPanel(parent, model, generalSettings, firmware), timer(timer), ui(new Ui::Timer) { ui->setupUi(this); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &TimerPanel::onModelDataAboutToBeUpdated); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &TimerPanel::onModelDataUpdateComplete); + connect(rawSwitchFilteredModel, &FilteredItemModel::aboutToBeUpdated, this, &TimerPanel::onItemModelAboutToBeUpdated); + connect(rawSwitchFilteredModel, &FilteredItemModel::updateComplete, this, &TimerPanel::onItemModelUpdateComplete); lock = true; @@ -172,12 +173,12 @@ void TimerPanel::on_name_editingFinished() } } -void TimerPanel::onModelDataAboutToBeUpdated() +void TimerPanel::onItemModelAboutToBeUpdated() { lock = true; } -void TimerPanel::onModelDataUpdateComplete() +void TimerPanel::onItemModelUpdateComplete() { update(); lock = false; @@ -991,13 +992,15 @@ void ModulePanel::onClearAccessRxClicked() /******************************************************************************/ -SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels) : ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Setup), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels) { ui->setupUi(this); - rawSwitchFilteredModel = new RawItemFilteredModel(commonItemModels->rawSwitchItemModel(), RawSwitch::TimersContext, this); + FILTEREDITEMMODEL(rawSwitchFilteredModel, SetupPanel, RawSwitchId, RawSwitch::TimersContext) + FILTEREDITEMMODELNOFLAGS(thrSourceFilteredModel, SetupPanel, ThrSourceId) Board::Type board = firmware->getBoard(); @@ -1248,6 +1251,8 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge SetupPanel::~SetupPanel() { delete ui; + delete rawSwitchFilteredModel; + delete thrSourceFilteredModel; } void SetupPanel::on_extendedLimits_toggled(bool checked) @@ -1750,7 +1755,15 @@ void SetupPanel::onTimerNameChanged() updateItemModels(); } +void SetupPanel::onItemModelAboutToBeUpdated() +{ +} + +void SetupPanel::onItemModelUpdateComplete() +{ +} + void SetupPanel::updateItemModels() { - commonItemModels->update(CommonItemModels::RMO_TIMERS); + sharedItemModels->update(AbstractItemModel::TimersUpdated); } diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index c56e63ea1..a4b7e7f81 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -26,8 +26,8 @@ constexpr char MIMETYPE_TIMER[] = "application/x-companion-timer"; -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; namespace Ui { class Setup; @@ -40,7 +40,8 @@ class TimerPanel : public ModelPanel Q_OBJECT public: - TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, QWidget *prevFocus, RawItemFilteredModel * switchModel); + TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, + QWidget *prevFocus, FilteredItemModel * switchModel); virtual ~TimerPanel(); virtual void update(); @@ -51,8 +52,8 @@ class TimerPanel : public ModelPanel void on_value_editingFinished(); void on_minuteBeep_toggled(bool checked); void on_name_editingFinished(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); signals: void nameChanged(); @@ -131,7 +132,7 @@ class SetupPanel : public ModelPanel Q_OBJECT public: - SetupPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + SetupPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); virtual ~SetupPanel(); virtual void update(); @@ -170,6 +171,8 @@ class SetupPanel : public ModelPanel void cmTimerMoveDown(); void cmTimerMoveUp(); void onTimerNameChanged(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: Ui::Setup *ui; @@ -191,8 +194,9 @@ class SetupPanel : public ModelPanel bool moveTimerDownAllowed() const; bool moveTimerUpAllowed() const; void swapTimerData(int idx1, int idx2); - CommonItemModels * commonItemModels; - RawItemFilteredModel * rawSwitchFilteredModel; + ItemModelsFactory * sharedItemModels; + FilteredItemModel * rawSwitchFilteredModel; + FilteredItemModel * thrSourceFilteredModel; void updateItemModels(); }; diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index e980c3943..3e1a5c49b 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -24,24 +24,31 @@ #include "ui_telemetry_sensor.h" #include "helpers.h" #include "appdata.h" -#include "rawitemfilteredmodel.h" #include -TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware, RawItemFilteredModel * rawSourceModel): +constexpr char FIM_NAME_RAWSOURCE[] {"Raw Source"}; +constexpr char FIM_NAME_TELEALLSRC[] {"Tele All Source"}; +constexpr char FIM_NAME_TELEPOSSRC[] {"Tele Pos Source"}; + +TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, + Firmware * firmware, const bool & parentLock, FilteredItemModelsFactory * localFilteredItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetryCustomScreen), - screen(screen) + screen(screen), + modelsUpdateCnt(0), + parentLock(parentLock) { ui->setupUi(this); - connect(rawSourceModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &TelemetryCustomScreen::onModelDataAboutToBeUpdated); - connect(rawSourceModel, &RawItemFilteredModel::dataUpdateComplete, this, &TelemetryCustomScreen::onModelDataUpdateComplete); + + FilteredItemModel * rawSourceFilteredModel = localFilteredItemModels->getItemModel(FIM_NAME_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(rawSourceModel); + 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))); } @@ -50,7 +57,7 @@ TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensBars); l++) { barsCB[l] = new QComboBox(this); barsCB[l]->setProperty("index", l); - barsCB[l]->setModel(rawSourceModel); + barsCB[l]->setModel(rawSourceFilteredModel); connect(barsCB[l], SIGNAL(activated(int)), this, SLOT(barSourceChanged(int))); ui->screenBarsLayout->addWidget(barsCB[l], l, 0, 1, 1); @@ -203,7 +210,7 @@ void TelemetryCustomScreen::updateBar(int line) void TelemetryCustomScreen::on_screenType_currentIndexChanged(int index) { - if (!lock) { + if (!isLocked()) { memset(reinterpret_cast(&screen.body), 0, sizeof(screen.body)); update(); emit modified(); @@ -212,7 +219,7 @@ void TelemetryCustomScreen::on_screenType_currentIndexChanged(int index) void TelemetryCustomScreen::scriptNameEdited() { - if (!lock) { + if (!isLocked()) { lock = true; Helpers::getFileComboBoxValue(ui->scriptName, screen.body.script.filename, 8); emit modified(); @@ -222,7 +229,7 @@ void TelemetryCustomScreen::scriptNameEdited() void TelemetryCustomScreen::customFieldChanged(int value) { - if (lock || !sender() || !qobject_cast(sender())) + if (isLocked() || !sender() || !qobject_cast(sender())) return; bool ok; @@ -238,7 +245,7 @@ void TelemetryCustomScreen::customFieldChanged(int value) void TelemetryCustomScreen::barSourceChanged(int value) { - if (lock || !sender() || !qobject_cast(sender())) + if (isLocked() || !sender() || !qobject_cast(sender())) return; bool ok; @@ -256,7 +263,7 @@ void TelemetryCustomScreen::barSourceChanged(int value) void TelemetryCustomScreen::barMinChanged(double value) { - if (!lock) { + if (!isLocked()) { int line = sender()->property("index").toInt(); screen.body.bars[line].barMin = round(value / minSB[line]->singleStep()); // TODO set min (maxSB) @@ -266,7 +273,7 @@ void TelemetryCustomScreen::barMinChanged(double value) void TelemetryCustomScreen::barMaxChanged(double value) { - if (!lock) { + if (!isLocked()) { int line = sender()->property("index").toInt(); screen.body.bars[line].barMax = round((value) / maxSB[line]->singleStep()); // TODO set max (minSB) @@ -276,7 +283,7 @@ void TelemetryCustomScreen::barMaxChanged(double value) void TelemetryCustomScreen::barTimeChanged() { - if (!lock) { + 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()); @@ -289,29 +296,49 @@ void TelemetryCustomScreen::barTimeChanged() } } -void TelemetryCustomScreen::onModelDataAboutToBeUpdated() +void TelemetryCustomScreen::connectItemModelEvents(const FilteredItemModel * itemModel) { - lock = true; + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TelemetryCustomScreen::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &TelemetryCustomScreen::onItemModelUpdateComplete); } -void TelemetryCustomScreen::onModelDataUpdateComplete() +void TelemetryCustomScreen::onItemModelAboutToBeUpdated() { - update(); - lock = false; + 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): +TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, + GeneralSettings & generalSettings, Firmware * firmware, const bool & parentLock, + FilteredItemModelsFactory * localFilteredItemModels) : ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetrySensor), sensor(sensor), - lock(false), + //lock(false), sensorIndex(sensorIndex), selectedIndex(0), - sensorCapability(sensorCapability) + sensorCapability(sensorCapability), + modelsUpdateCnt(0), + parentLock(parentLock) { ui->setupUi(this); + + FilteredItemModel * teleAllSrcFilteredModel = localFilteredItemModels->getItemModel(FIM_NAME_TELEALLSRC); + connectItemModelEvents(teleAllSrcFilteredModel); + FilteredItemModel * telePosSrcFilteredModel = localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC); + connectItemModelEvents(telePosSrcFilteredModel); + ui->numLabel->setText(tr("TELE%1").arg(sensorIndex + 1)); ui->numLabel->setProperty("index", sensorIndex); ui->numLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); @@ -333,9 +360,13 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ui->logs->setField(sensor.logs, this); ui->persistent->setField(sensor.persistent, this); ui->onlyPositive->setField(sensor.onlyPositive, this); + ui->gpsSensor->setModel(telePosSrcFilteredModel); ui->gpsSensor->setField(sensor.gps, this); + ui->altSensor->setModel(telePosSrcFilteredModel); ui->altSensor->setField(sensor.alt, this); + ui->ampsSensor->setModel(telePosSrcFilteredModel); ui->ampsSensor->setField(sensor.amps, this); + ui->cellsSensor->setModel(telePosSrcFilteredModel); ui->cellsSensor->setField(sensor.source, this); ui->cellsIndex->addItem(tr("Lowest"), SensorData::TELEM_CELL_INDEX_LOWEST); for (int i = SensorData::TELEM_CELL_INDEX_LOWEST + 1; i < SensorData::TELEM_CELL_INDEX_HIGHEST; i++) @@ -343,9 +374,13 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ui->cellsIndex->addItem(tr("Highest"), SensorData::TELEM_CELL_INDEX_HIGHEST); ui->cellsIndex->addItem(tr("Delta"), SensorData::TELEM_CELL_INDEX_DELTA); ui->cellsIndex->setField(sensor.index); + ui->source1->setModel(teleAllSrcFilteredModel); ui->source1->setField(sensor.sources[0], this); + ui->source2->setModel(teleAllSrcFilteredModel); ui->source2->setField(sensor.sources[1], this); + ui->source3->setModel(teleAllSrcFilteredModel); ui->source3->setField(sensor.sources[2], this); + ui->source4->setModel(teleAllSrcFilteredModel); ui->source4->setField(sensor.sources[3], this); ui->prec->setField(sensor.prec, 0, 2, false, "", this); update(); @@ -404,14 +439,10 @@ void TelemetrySensorPanel::update() sources12FieldsDisplayed = (sensor.formula <= SensorData::TELEM_FORMULA_MULTIPLY); sources34FieldsDisplayed = (sensor.formula < SensorData::TELEM_FORMULA_MULTIPLY); totalizeFieldsDisplayed = (sensor.formula == SensorData::TELEM_FORMULA_TOTALIZE); - updateSourcesComboBox(ui->source1, true); - updateSourcesComboBox(ui->source2, true); - updateSourcesComboBox(ui->source3, true); - updateSourcesComboBox(ui->source4, true); - updateSourcesComboBox(ui->gpsSensor, false); - updateSourcesComboBox(ui->altSensor, false); - updateSourcesComboBox(ui->ampsSensor, false); - updateSourcesComboBox(ui->cellsSensor, false); + ui->source1->updateValue(); + ui->source2->updateValue(); + ui->source3->updateValue(); + ui->source4->updateValue(); } else { ui->idLabel->show(); @@ -483,40 +514,9 @@ void TelemetrySensorPanel::update() lock = false; } -void populateTelemetrySourcesComboBox(AutoComboBox * cb, const ModelData * model, bool negative) -{ - cb->clear(); - if (negative) { - for (int i = -CPN_MAX_SENSORS; i < 0; ++i) { - const SensorData& sensor = model->sensorData[-i - 1]; - if (sensor.isAvailable()) { - if (sensor.type == SensorData::TELEM_TYPE_CUSTOM) - cb->addItem(QString("-%1 (%2)").arg(sensor.label, sensor.getOrigin(model)), i); - else - cb->addItem(QString("-%1").arg(sensor.label), i); - } - } - } - cb->addItem("---", 0); - for (unsigned i = 1; i <= CPN_MAX_SENSORS; ++i) { - const SensorData& sensor = model->sensorData[i-1]; - if (sensor.isAvailable()) { - if (sensor.type == SensorData::TELEM_TYPE_CUSTOM) - cb->addItem(QString("%1 (%2)").arg(sensor.label, sensor.getOrigin(model)), i); - else - cb->addItem(QString("%1").arg(sensor.label), i); - } - } -} - -void TelemetrySensorPanel::updateSourcesComboBox(AutoComboBox * cb, bool negative) -{ - populateTelemetrySourcesComboBox(cb, model, negative); -} - void TelemetrySensorPanel::on_name_editingFinished() { - if (!lock) { + if (!isLocked() && (sensor.label != ui->name->text().toLatin1())) { strcpy(sensor.label, ui->name->text().toLatin1()); emit dataModified(); } @@ -524,7 +524,7 @@ void TelemetrySensorPanel::on_name_editingFinished() void TelemetrySensorPanel::on_type_currentIndexChanged(int index) { - if (!lock) { + if (!isLocked()) { sensor.type = index; update(); emit modified(); @@ -533,7 +533,7 @@ void TelemetrySensorPanel::on_type_currentIndexChanged(int index) void TelemetrySensorPanel::on_formula_currentIndexChanged(int index) { - if (!lock) { + if (!isLocked()) { sensor.formula = index; if (sensor.formula == SensorData::TELEM_FORMULA_CELL) { sensor.prec = 2; @@ -553,7 +553,7 @@ void TelemetrySensorPanel::on_formula_currentIndexChanged(int index) void TelemetrySensorPanel::on_unit_currentIndexChanged(int index) { - if (!lock) { + if (!isLocked()) { sensor.unit = index; if (sensor.unit == SensorData::UNIT_FAHRENHEIT) { sensor.prec = 0; @@ -565,7 +565,7 @@ void TelemetrySensorPanel::on_unit_currentIndexChanged(int index) void TelemetrySensorPanel::on_prec_valueChanged() { - if (!lock) { + if (!isLocked()) { update(); } } @@ -686,15 +686,52 @@ void TelemetrySensorPanel::cmMoveDown() emit moveDownSensor(selectedIndex); } +void TelemetrySensorPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TelemetrySensorPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &TelemetrySensorPanel::onItemModelUpdateComplete); +} + +void TelemetrySensorPanel::onItemModelAboutToBeUpdated() +{ + lock = true; + modelsUpdateCnt++; +} + +void TelemetrySensorPanel::onItemModelUpdateComplete() +{ + modelsUpdateCnt--; + if (modelsUpdateCnt < 1) { + // leave updating to parent + lock = false; + } +} + /******************************************************/ -TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Telemetry), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels), + modelsUpdateCnt(0) { ui->setupUi(this); - rawSourceFilteredModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), this); + + localFilteredItemModels = new FilteredItemModelsFactory(); + int newid; + + newid = localFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::RawSourceId)), + FIM_NAME_RAWSOURCE); + connectItemModelEvents(localFilteredItemModels->getItemModel(newid)); + + newid = localFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::TeleSourceId)), + FIM_NAME_TELEALLSRC); + connectItemModelEvents(localFilteredItemModels->getItemModel(newid)); + + newid = localFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::TeleSourceId), + FilteredItemModel::PositiveFilter), FIM_NAME_TELEPOSSRC); + connectItemModelEvents(localFilteredItemModels->getItemModel(newid)); sensorCapability = firmware->getCapability(Sensors); if (sensorCapability > CPN_MAX_SENSORS) // TODO should be role of getCapability @@ -704,13 +741,15 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin model.frsky.usrProto = 1; } + ui->varioSource->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); ui->varioSource->setField(model.frsky.varioSource, this); ui->varioCenterSilent->setField(model.frsky.varioCenterSilent, this); ui->A1GB->hide(); ui->A2GB->hide(); for (int i = 0; i < sensorCapability; ++i) { - TelemetrySensorPanel * panel = new TelemetrySensorPanel(this, model.sensorData[i], i, sensorCapability, model, generalSettings, firmware); + TelemetrySensorPanel * panel = new TelemetrySensorPanel(this, model.sensorData[i], i, sensorCapability, model, generalSettings, + firmware, lock, localFilteredItemModels); ui->sensorsLayout->addWidget(panel); sensorPanels[i] = panel; connect(panel, SIGNAL(dataModified()), this, SLOT(on_dataModifiedSensor())); @@ -723,7 +762,9 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin } if (IS_TARANIS_X9(firmware->getBoard())) { + ui->voltsSource->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); ui->voltsSource->setField(model.frsky.voltsSource, this); + ui->altitudeSource->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); ui->altitudeSource->setField(model.frsky.altitudeSource, this); } else { @@ -731,7 +772,8 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin } for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) { - TelemetryCustomScreen * tab = new TelemetryCustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, rawSourceFilteredModel); + TelemetryCustomScreen * tab = new TelemetryCustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, lock, + localFilteredItemModels); ui->customScreens->addTab(tab, tr("Telemetry screen %1").arg(i + 1)); telemetryCustomScreens[i] = tab; connect(tab, &TelemetryCustomScreen::modified, this, &TelemetryPanel::onModified); @@ -745,10 +787,13 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin TelemetryPanel::~TelemetryPanel() { delete ui; + delete localFilteredItemModels; } void TelemetryPanel::update() { + lock = true; + if (IS_HORUS_OR_TARANIS(firmware->getBoard())) { if (model->moduleData[0].protocol == PULSES_OFF && model->moduleData[1].protocol == PULSES_PPM) { ui->telemetryProtocol->setEnabled(true); @@ -758,10 +803,10 @@ void TelemetryPanel::update() ui->telemetryProtocol->setCurrentIndex(0); } - populateTelemetrySourcesComboBox(ui->rssiSourceCB, model, false); - populateTelemetrySourcesComboBox(ui->voltsSource, model, false); - populateTelemetrySourcesComboBox(ui->altitudeSource, model, false); - populateTelemetrySourcesComboBox(ui->varioSource, model, false); + ui->rssiSourceCB->updateValue(); + ui->voltsSource->updateValue(); + ui->altitudeSource->updateValue(); + ui->varioSource->updateValue(); } for (int i = 0; i < sensorCapability; ++i) { @@ -771,6 +816,8 @@ void TelemetryPanel::update() for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) { telemetryCustomScreens[i]->update(); } + + lock = false; } void TelemetryPanel::setup() @@ -792,9 +839,9 @@ void TelemetryPanel::setup() ui->rssiSourceLabel->show(); ui->rssiSourceLabel->setText(tr("Source")); + ui->rssiSourceCB->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); ui->rssiSourceCB->setField(model->rssiSource, this); ui->rssiSourceCB->show(); - populateTelemetrySourcesComboBox(ui->rssiSourceCB, model, false); ui->rssiAlarmWarningCB->hide(); ui->rssiAlarmCriticalCB->hide(); @@ -842,44 +889,9 @@ void TelemetryPanel::setup() lock = false; } -void TelemetryPanel::populateVarioSource() -{ - AutoComboBox * cb = ui->varioSource; - cb->setField(model->frsky.varioSource, this); - cb->addItem(tr("Alti"), TELEMETRY_VARIO_SOURCE_ALTI); - cb->addItem(tr("Alti+"), TELEMETRY_VARIO_SOURCE_ALTI_PLUS); - cb->addItem(tr("VSpeed"), TELEMETRY_VARIO_SOURCE_VSPEED); - cb->addItem(tr("A1"), TELEMETRY_VARIO_SOURCE_A1); - cb->addItem(tr("A2"), TELEMETRY_VARIO_SOURCE_A2); -} - -void TelemetryPanel::populateVoltsSource() -{ - AutoComboBox * cb = ui->frskyVoltCB; - cb->setField(model->frsky.voltsSource, this); - cb->addItem(tr("A1"), TELEMETRY_VOLTS_SOURCE_A1); - cb->addItem(tr("A2"), TELEMETRY_VOLTS_SOURCE_A2); - cb->addItem(tr("A3"), TELEMETRY_VOLTS_SOURCE_A3); - cb->addItem(tr("A4"), TELEMETRY_VOLTS_SOURCE_A4); - cb->addItem(tr("FAS"), TELEMETRY_VOLTS_SOURCE_FAS); - cb->addItem(tr("Cells"), TELEMETRY_VOLTS_SOURCE_CELLS); -} - -void TelemetryPanel::populateCurrentSource() -{ - AutoComboBox * cb = ui->frskyCurrentCB; - cb->setField(model->frsky.currentSource, this); - cb->addItem(tr("---"), TELEMETRY_CURRENT_SOURCE_NONE); - cb->addItem(tr("A1"), TELEMETRY_CURRENT_SOURCE_A1); - cb->addItem(tr("A2"), TELEMETRY_CURRENT_SOURCE_A2); - cb->addItem(tr("A3"), TELEMETRY_CURRENT_SOURCE_A3); - cb->addItem(tr("A4"), TELEMETRY_CURRENT_SOURCE_A4); - cb->addItem(tr("FAS"), TELEMETRY_CURRENT_SOURCE_FAS); -} - void TelemetryPanel::on_telemetryProtocol_currentIndexChanged(int index) { - if (!lock) { + if (!isLocked()) { model->telemetryProtocol = index; emit modified(); } @@ -892,7 +904,7 @@ void TelemetryPanel::onModified() void TelemetryPanel::on_bladesCount_editingFinished() { - if (!lock) { + if (!isLocked()) { model->frsky.blades = ui->bladesCount->value(); emit modified(); } @@ -900,7 +912,7 @@ void TelemetryPanel::on_bladesCount_editingFinished() void TelemetryPanel::on_frskyProtoCB_currentIndexChanged(int index) { - if (!lock) { + if (!isLocked()) { model->frsky.usrProto = index; for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) telemetryCustomScreens[i]->update(); @@ -910,31 +922,39 @@ void TelemetryPanel::on_frskyProtoCB_currentIndexChanged(int index) void TelemetryPanel::on_rssiAlarmWarningSB_editingFinished() { - model->rssiAlarms.warning= ui->rssiAlarmWarningSB->value(); - emit modified(); + if (!isLocked()) { + model->rssiAlarms.warning= ui->rssiAlarmWarningSB->value(); + emit modified(); + } } void TelemetryPanel::on_rssiAlarmCriticalSB_editingFinished() { - model->rssiAlarms.critical = ui->rssiAlarmCriticalSB->value(); - emit modified(); + if (!isLocked()) { + model->rssiAlarms.critical = ui->rssiAlarmCriticalSB->value(); + emit modified(); + } } void TelemetryPanel::on_varioLimitMin_DSB_editingFinished() { - model->frsky.varioMin = round(ui->varioLimitMin_DSB->value() + 10); - emit modified(); + if (!isLocked()) { + model->frsky.varioMin = round(ui->varioLimitMin_DSB->value() + 10); + emit modified(); + } } void TelemetryPanel::on_varioLimitMax_DSB_editingFinished() { - model->frsky.varioMax = round(ui->varioLimitMax_DSB->value() - 10); - emit modified(); + if (!isLocked()) { + model->frsky.varioMax = round(ui->varioLimitMax_DSB->value() - 10); + emit modified(); + } } void TelemetryPanel::on_varioLimitCenterMin_DSB_editingFinished() { - if (!lock) { + if (!isLocked()) { if (ui->varioLimitCenterMin_DSB->value() > ui->varioLimitCenterMax_DSB->value()) { ui->varioLimitCenterMax_DSB->setValue(ui->varioLimitCenterMin_DSB->value()); } @@ -945,7 +965,7 @@ void TelemetryPanel::on_varioLimitCenterMin_DSB_editingFinished() void TelemetryPanel::on_varioLimitCenterMax_DSB_editingFinished() { - if (!lock) { + if (!isLocked()) { if (ui->varioLimitCenterMin_DSB->value() > ui->varioLimitCenterMax_DSB->value()) { ui->varioLimitCenterMax_DSB->setValue(ui->varioLimitCenterMin_DSB->value()); } @@ -956,21 +976,27 @@ void TelemetryPanel::on_varioLimitCenterMax_DSB_editingFinished() void TelemetryPanel::on_fasOffset_DSB_editingFinished() { - model->frsky.fasOffset = ui->fasOffset_DSB->value() * 10; - emit modified(); + if (!isLocked()) { + model->frsky.fasOffset = ui->fasOffset_DSB->value() * 10; + emit modified(); + } } void TelemetryPanel::on_mahCount_SB_editingFinished() { - model->frsky.storedMah = ui->mahCount_SB->value(); - emit modified(); + if (!isLocked()) { + model->frsky.storedMah = ui->mahCount_SB->value(); + emit modified(); + } } void TelemetryPanel::on_mahCount_ChkB_toggled(bool checked) { - model->frsky.mAhPersistent = checked; - ui->mahCount_SB->setDisabled(!checked); - emit modified(); + if (!isLocked()) { + model->frsky.mAhPersistent = checked; + ui->mahCount_SB->setDisabled(!checked); + emit modified(); + } } void TelemetryPanel::on_clearAllSensors() @@ -980,9 +1006,7 @@ void TelemetryPanel::on_clearAllSensors() model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_CLEAR, i); } - updateItemModels(); - update(); - emit modified(); + on_dataModifiedSensor(); } void TelemetryPanel::on_insertSensor(int selectedIndex) @@ -990,10 +1014,7 @@ void TelemetryPanel::on_insertSensor(int selectedIndex) memmove(&model->sensorData[selectedIndex + 1], &model->sensorData[selectedIndex], (CPN_MAX_SENSORS - (selectedIndex + 1)) * sizeof(SensorData)); model->sensorData[selectedIndex].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, 1); - - updateItemModels(); - update(); - emit modified(); + on_dataModifiedSensor(); } void TelemetryPanel::on_deleteSensor(int selectedIndex) @@ -1004,10 +1025,7 @@ void TelemetryPanel::on_deleteSensor(int selectedIndex) memmove(&model->sensorData[selectedIndex], &model->sensorData[selectedIndex + 1], (CPN_MAX_SENSORS - (selectedIndex + 1)) * sizeof(SensorData)); model->sensorData[CPN_MAX_SENSORS - 1].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, -1); - - updateItemModels(); - update(); - emit modified(); + on_dataModifiedSensor(); } void TelemetryPanel::on_moveUpSensor(int selectedIndex) @@ -1029,20 +1047,33 @@ void TelemetryPanel::swapData(int idx1, int idx2) memcpy(sd2, sd1, sizeof(SensorData)); memcpy(sd1, &sdtmp, sizeof(SensorData)); model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_SWAP, idx1, idx2); - updateItemModels(); - update(); - emit modified(); + on_dataModifiedSensor(); } } void TelemetryPanel::on_dataModifiedSensor() { - updateItemModels(); - update(); + sharedItemModels->update(AbstractItemModel::TeleSensorsUpdated); emit modified(); } -void TelemetryPanel::updateItemModels() +void TelemetryPanel::connectItemModelEvents(const FilteredItemModel * itemModel) { - commonItemModels->update(CommonItemModels::RMO_TELEMETRY_SENSORS); + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TelemetryPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &TelemetryPanel::onItemModelUpdateComplete); +} + +void TelemetryPanel::onItemModelAboutToBeUpdated() +{ + lock = true; + modelsUpdateCnt++; +} + +void TelemetryPanel::onItemModelUpdateComplete() +{ + modelsUpdateCnt--; + if (modelsUpdateCnt < 1) { + update(); + lock = false; + } } diff --git a/companion/src/modeledit/telemetry.h b/companion/src/modeledit/telemetry.h index 9719c7df6..51273d867 100644 --- a/companion/src/modeledit/telemetry.h +++ b/companion/src/modeledit/telemetry.h @@ -23,12 +23,11 @@ #include "modeledit.h" #include "eeprominterface.h" +#include "rawitemfilteredmodel.h" -constexpr char MIMETYPE_TELE_SENSOR[] = "application/x-companion-tele-sensor"; +constexpr char MIMETYPE_TELE_SENSOR[] {"application/x-companion-tele-sensor"}; class AutoComboBox; -class CommonItemModels; -class RawItemFilteredModel; class TimerEdit; namespace Ui { @@ -42,7 +41,8 @@ class TelemetryCustomScreen: public ModelPanel Q_OBJECT public: - TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware, RawItemFilteredModel * rawSourceModel); + TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware, + const bool & parentLock, FilteredItemModelsFactory * localFilteredItemModels); ~TelemetryCustomScreen(); void update(); @@ -54,8 +54,8 @@ class TelemetryCustomScreen: public ModelPanel void barMinChanged(double value); void barMaxChanged(double value); void barTimeChanged(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: void updateBar(int line); @@ -67,6 +67,11 @@ class TelemetryCustomScreen: public ModelPanel 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 @@ -74,7 +79,9 @@ class TelemetrySensorPanel: public ModelPanel Q_OBJECT public: - TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware); + TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, + GeneralSettings & generalSettings, Firmware * firmware, const bool & parentLock, + FilteredItemModelsFactory * localFilteredItemModels); ~TelemetrySensorPanel(); void update(); @@ -86,7 +93,6 @@ class TelemetrySensorPanel: public ModelPanel void moveUpSensor(int index); void moveDownSensor(int index); - protected slots: void on_name_editingFinished(); void on_type_currentIndexChanged(int index); @@ -104,20 +110,24 @@ class TelemetrySensorPanel: public ModelPanel void cmDelete(); void cmMoveUp(); void cmMoveDown(); - - protected: - void updateSourcesComboBox(AutoComboBox * cb, bool negative); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: Ui::TelemetrySensor * ui; SensorData & sensor; - bool lock = false; + //bool lock = false; int sensorIndex = 0; int selectedIndex = 0; int sensorCapability; bool insertAllowed() const; bool moveDownAllowed() const; bool moveUpAllowed() const; + int modelsUpdateCnt; + const bool &parentLock; + + inline bool isLocked() { return lock | parentLock; } + void connectItemModelEvents(const FilteredItemModel * itemModel); }; class TelemetryPanel : public ModelPanel @@ -125,7 +135,8 @@ class TelemetryPanel : public ModelPanel Q_OBJECT public: - TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels); virtual ~TelemetryPanel(); virtual void update(); @@ -152,22 +163,23 @@ class TelemetryPanel : public ModelPanel void on_moveUpSensor(int index); void on_moveDownSensor(int index); void on_dataModifiedSensor(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: Ui::Telemetry *ui; - TelemetryCustomScreen * telemetryCustomScreens[4]; - TelemetrySensorPanel * sensorPanels[CPN_MAX_SENSORS]; + TelemetryCustomScreen *telemetryCustomScreens[4]; + TelemetrySensorPanel *sensorPanels[CPN_MAX_SENSORS]; int sensorCapability; - CommonItemModels * commonItemModels; - RawItemFilteredModel * rawSourceFilteredModel; + ItemModelsFactory *sharedItemModels; + FilteredItemModelsFactory *localFilteredItemModels; + int modelsUpdateCnt; void setup(); void telBarUpdate(); - void populateVoltsSource(); - void populateCurrentSource(); - void populateVarioSource(); void swapData(int idx1, int idx2); - void updateItemModels(); + void connectItemModelEvents(const FilteredItemModel * itemModel); + inline bool isLocked() { return lock; } }; #endif // _TELEMETRY_H_ diff --git a/companion/src/shared/autocombobox.h b/companion/src/shared/autocombobox.h index fcb1845ac..158352f97 100644 --- a/companion/src/shared/autocombobox.h +++ b/companion/src/shared/autocombobox.h @@ -37,30 +37,37 @@ class AutoComboBox: public QComboBox void clear() { - lock = true; - QComboBox::clear(); - next = 0; - lock = false; + if (!hasModel) { + lock = true; + QComboBox::clear(); + next = 0; + lock = false; + } } virtual void insertItems(int index, const QStringList & items) { - foreach(QString item, items) { - addItem(item); + if (!hasModel) { + foreach(QString item, items) { + addItem(item); + } } } virtual void addItem(const QString & item) { - addItem(item, next++); + if (!hasModel) + addItem(item, next++); } virtual void addItem(const QString & item, int value) { - lock = true; - QComboBox::addItem(item, value); - lock = false; - updateValue(); + if (!hasModel) { + lock = true; + QComboBox::addItem(item, value); + lock = false; + updateValue(); + } } void setField(unsigned int & field, GenericPanel * panel=nullptr) @@ -77,11 +84,22 @@ class AutoComboBox: public QComboBox updateValue(); } + void setModel(QAbstractItemModel * model) + { + lock = true; + QComboBox::setModel(model); + lock = false; + hasModel = true; + updateValue(); + } + void setAutoIndexes() { - for (int i=0; imodified(); + if (panel && panel->lock) + return; + if (index > -1) { + const int val = itemData(index).toInt(); + if (field && !lock) { + *field = val; + if (panel) + emit panel->modified(); + } + emit currentDataChanged(val); } - emit currentDataChanged(val); } protected: @@ -113,6 +135,7 @@ class AutoComboBox: public QComboBox GenericPanel * panel = nullptr; int next = 0; bool lock = false; + bool hasModel = false; }; #endif // _AUTOCOMBOBOX_H_ From bcc3d64ef036a1369d2d0715bcdb35006a258715 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 4 Jan 2021 16:04:20 +0100 Subject: [PATCH 11/65] Fix some elements of colorlcd simu not working --- radio/src/telemetry/telemetry.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 25e335042..7e085488e 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -290,7 +290,9 @@ void telemetryInit(uint8_t protocol) #if defined(LUA) outputTelemetryBuffer.reset(); #endif +#if !defined(SIMU) telemetryPortSetDirectionInput(); +#endif } else if (protocol == PROTOCOL_TELEMETRY_SPEKTRUM) { // Spektrum's own small race RX (SPM4648) uses 125000 8N1, use the same since there is no real standard From bae86dc85f1af7a1d7698c3b5cb3b50e9d5f3ac5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 4 Jan 2021 16:44:26 +0100 Subject: [PATCH 12/65] Fix --- radio/src/gui/gui_common.cpp | 2 +- radio/src/trainer.cpp | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 1d856b6f7..57e416f6e 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -554,7 +554,7 @@ bool isPxx2IsrmChannelsCountAllowed(int channels) bool isTrainerUsingModuleBay() { #if defined(PCBTARANIS) - if (TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE <= g_model.trainerData.mode && g_model.trainerData.mode <= TRAINER_MODE_MASTER_BATTERY_COMPARTMENT) + if (TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE <= g_model.trainerData.mode && g_model.trainerData.mode <= TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE) return true; #endif return false; diff --git a/radio/src/trainer.cpp b/radio/src/trainer.cpp index 893d42b67..c4c3c0c4f 100644 --- a/radio/src/trainer.cpp +++ b/radio/src/trainer.cpp @@ -26,8 +26,8 @@ uint8_t currentTrainerMode = 0xff; void checkTrainerSignalWarning() { - enum PpmInValidState_t { - PPM_IN_IS_NOT_USED=0, + enum { + PPM_IN_IS_NOT_USED = 0, PPM_IN_IS_VALID, PPM_IN_INVALID }; @@ -137,21 +137,20 @@ void checkTrainerSettings() #if defined(TRAINER_BATTERY_COMPARTMENT) case TRAINER_MODE_MASTER_BATTERY_COMPARTMENT: #if defined(AUX_SERIAL) - if (g_eeGeneral.auxSerialMode == UART_MODE_SBUS_TRAINER) + if (g_eeGeneral.auxSerialMode == UART_MODE_SBUS_TRAINER) { auxSerialSbusInit(); - else - init_trainer_capture(); - break; + break; + } #endif + #if defined(AUX2_SERIAL) - if (g_eeGeneral.aux2SerialMode == UART_MODE_SBUS_TRAINER) + if (g_eeGeneral.aux2SerialMode == UART_MODE_SBUS_TRAINER) { aux2SerialSbusInit(); - else - init_trainer_capture(); - break; + break; + } #endif - init_trainer_capture(); - break; + + // no break #endif case TRAINER_MODE_MASTER_TRAINER_JACK: From 86619150b299f08449e2d636d65f393281e4e4a1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 4 Jan 2021 18:59:22 +0100 Subject: [PATCH 13/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 5fb57cece..ebbebc904 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2069,3 +2069,4 @@ Raoul Piccioli Uwe Probst Glendon Satchell Trevor Collins +Horst Grashauser From 6da4766e56ee2a7135eeac580e2d8a930165e192 Mon Sep 17 00:00:00 2001 From: MRC3742 Date: Mon, 4 Jan 2021 14:27:52 -0500 Subject: [PATCH 14/65] MPM Updates (#8197) --- companion/src/firmwares/moduledata.h | 5 +++-- companion/src/firmwares/multiprotocols.cpp | 12 +++++++----- radio/src/gui/gui_common.cpp | 11 ++++++----- radio/src/pulses/modules_constants.h | 5 +++-- radio/src/translations/untranslated.h | 2 +- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/companion/src/firmwares/moduledata.h b/companion/src/firmwares/moduledata.h index 550509a46..119f44eb8 100644 --- a/companion/src/firmwares/moduledata.h +++ b/companion/src/firmwares/moduledata.h @@ -141,8 +141,9 @@ enum MultiModuleRFProtocols { MODULE_SUBTYPE_MULTI_REALACC, MODULE_SUBTYPE_MULTI_OMP, MODULE_SUBTYPE_MULTI_MLINK, - MODULE_SUBTYPE_MULTI_WFLYRF, - MODULE_SUBTYPE_MULTI_LAST = MODULE_SUBTYPE_MULTI_WFLYRF + MODULE_SUBTYPE_MULTI_WFLY2, + MODULE_SUBTYPE_MULTI_E016HV2, + MODULE_SUBTYPE_MULTI_LAST = MODULE_SUBTYPE_MULTI_E016HV2 }; enum TrainerProtocol { diff --git a/companion/src/firmwares/multiprotocols.cpp b/companion/src/firmwares/multiprotocols.cpp index bc4fe4151..1aedbc7d5 100644 --- a/companion/src/firmwares/multiprotocols.cpp +++ b/companion/src/firmwares/multiprotocols.cpp @@ -90,10 +90,10 @@ static const QStringList STR_SUBTYPE_FRSKYL {"LR12", "LR12 6-Channel"}; static const QStringList STR_SUBTYPE_ESKY150V2 {"150 V2"}; static const QStringList STR_SUBTYPE_JJRC345 {"Standard", "SkyTumbler"}; static const QStringList STR_SUBTYPE_KYOSHO {"FHSS", "Hype"}; -static const QStringList STR_SUBTYPE_RLINK {"Surface", "Air"}; +static const QStringList STR_SUBTYPE_RLINK {"Surface", "Air", "DumboRC"}; static const QStringList STR_SUBTYPE_ELRS {"Not Available WIP"}; static const QStringList STR_SUBTYPE_REALACC {"R11"}; -static const QStringList STR_SUBTYPE_WFLYRF {"RF20x"}; +static const QStringList STR_SUBTYPE_WFLY2 {"RF20x"}; static const QStringList NO_SUBTYPE {STR_MULTI_DEFAULT}; @@ -159,11 +159,12 @@ const Multiprotocols multiProtocols { {MODULE_SUBTYPE_MULTI_JJRC345, 1, false, STR_SUBTYPE_JJRC345, nullptr}, {MODULE_SUBTYPE_MULTI_Q90C, 0, false, NO_SUBTYPE, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_KYOSHO, 1, false, STR_SUBTYPE_KYOSHO, nullptr}, - {MODULE_SUBTYPE_MULTI_RLINK, 1, false, STR_SUBTYPE_RLINK, STR_MULTI_RFTUNE}, + {MODULE_SUBTYPE_MULTI_RLINK, 2, false, STR_SUBTYPE_RLINK, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_ELRS, 0, false, STR_SUBTYPE_ELRS, nullptr}, {MODULE_SUBTYPE_MULTI_REALACC, 0, false, STR_SUBTYPE_REALACC, nullptr}, {MODULE_SUBTYPE_MULTI_OMP, 0, false, NO_SUBTYPE, STR_MULTI_RFTUNE}, - {MODULE_SUBTYPE_MULTI_WFLYRF, 0, false, STR_SUBTYPE_WFLYRF, nullptr}, + {MODULE_SUBTYPE_MULTI_WFLY2, 0, false, STR_SUBTYPE_WFLY2, STR_MULTI_OPTION}, + {MODULE_SUBTYPE_MULTI_E016HV2, 0, false, NO_SUBTYPE, STR_MULTI_RFTUNE}, {MM_RF_CUSTOM_SELECTED, 7, true, STR_SUBTYPE_CUSTOM, STR_MULTI_OPTION}, // Sentinel and default for protocols not listed above (MM_RF_CUSTOM is 0xff) @@ -208,7 +209,8 @@ QString Multiprotocols::protocolToString(int protocol, bool custom) "Hitec", "Wfly", "Bugs", "Bugs Mini", "Traxxas", "NCC-1701-A", "E01X", "WL Heli V911S", "GD00X", "Volantex V761", "KFPlan KF606", "Redpine", "Potensic", "ZSX", "Height", "Scanner", "FrSky RX", "FlySky AFHDS2A RX", "HoTT", "Fx816", "Bayang RX", "Pelikan", "Tiger", "XK", "XN297 Dump", "FrSky X 2.1", "FrSky R9", "Propel", "FrSky L", "Skyartec", - "ESky 150v2", "DSM RX", "JJRC345", "Q90C", "Kyosho", "RadioLink", "ExpressLRS", "Realacc", "OMP", "M-Link", "Wfly RF" + "ESky 150v2", "DSM RX", "JJRC345", "Q90C", "Kyosho", "RadioLink", "ExpressLRS", "Realacc", "OMP", "M-Link", "Wfly 2", + "E016H v2" }); return strings.value(protocol, CPN_STR_UNKNOWN_ITEM); diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 57e416f6e..368aac163 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -979,10 +979,10 @@ const char STR_SUBTYPE_FRSKYL[] = "\010""LR12\0 ""LR12 6ch"; const char STR_SUBTYPE_ESKY150V2[] = "\006""150 V2"; const char STR_SUBTYPE_JJRC345[] = "\007""Std\0 ""SkyTmbr"; const char STR_SUBTYPE_KYOSHO[] = "\004""FHSS""Hype"; -const char STR_SUBTYPE_RLINK[] = "\007""Surface""Air\0 "; +const char STR_SUBTYPE_RLINK[] = "\007""Surface""Air\0 ""DumboRC"; const char STR_SUBTYPE_ELRS[] = "\007""N/A WIP"; const char STR_SUBTYPE_REALACC[] = "\003""R11"; -const char STR_SUBTYPE_WFLYRF[] = "\005""RF20x"; +const char STR_SUBTYPE_WFLY2[] = "\005""RF20x"; const char* mm_options_strings::options[] = { nullptr, @@ -1045,7 +1045,7 @@ const mm_protocol_definition multi_protocols[] = { {MODULE_SUBTYPE_MULTI_FRSKYX_RX, 1, false, false, STR_SUBTYPE_FRSKYX_RX, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_HOTT, 1, true, false, STR_SUBTYPE_HOTT, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_FX816, 0, false, false, STR_SUBTYPE_FX816, nullptr}, - {MODULE_SUBTYPE_MULTI_PELIKAN, 1, false, false, STR_SUBTYPE_PELIKAN, nullptr}, + {MODULE_SUBTYPE_MULTI_PELIKAN, 1, false, true, STR_SUBTYPE_PELIKAN, nullptr}, {MODULE_SUBTYPE_MULTI_XK, 1, false, false, STR_SUBTYPE_XK, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_XN297DUMP, 4, false, false, STR_SUBTYPE_XN297DUMP, STR_MULTI_RFCHAN}, {MODULE_SUBTYPE_MULTI_FRSKYX2, 5, true, false, STR_SUBTYPE_FRSKYX2, STR_MULTI_RFTUNE}, @@ -1057,11 +1057,12 @@ const mm_protocol_definition multi_protocols[] = { {MODULE_SUBTYPE_MULTI_JJRC345, 1, false, false, STR_SUBTYPE_JJRC345, nullptr}, {MODULE_SUBTYPE_MULTI_Q90C, 0, false, false, NO_SUBTYPE, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_KYOSHO, 1, false, true, STR_SUBTYPE_KYOSHO, nullptr}, - {MODULE_SUBTYPE_MULTI_RLINK, 1, false, false, STR_SUBTYPE_RLINK, STR_MULTI_RFTUNE}, + {MODULE_SUBTYPE_MULTI_RLINK, 2, false, false, STR_SUBTYPE_RLINK, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_ELRS, 0, false, false, STR_SUBTYPE_ELRS, nullptr}, {MODULE_SUBTYPE_MULTI_REALACC, 0, false, false, STR_SUBTYPE_REALACC, nullptr}, {MODULE_SUBTYPE_MULTI_OMP, 0, false, false, NO_SUBTYPE, STR_MULTI_RFTUNE}, - {MODULE_SUBTYPE_MULTI_WFLYRF, 0, false, false, STR_SUBTYPE_WFLYRF, nullptr}, + {MODULE_SUBTYPE_MULTI_WFLY2, 0, false, false, STR_SUBTYPE_WFLY2, STR_MULTI_OPTION}, + {MODULE_SUBTYPE_MULTI_E016HV2, 0, false, false, NO_SUBTYPE, STR_MULTI_RFTUNE}, {MM_RF_CUSTOM_SELECTED, 7, true, true, NO_SUBTYPE, STR_MULTI_OPTION}, // Sentinel and default for protocols not listed above (MM_RF_CUSTOM is 0xff) diff --git a/radio/src/pulses/modules_constants.h b/radio/src/pulses/modules_constants.h index 8ff0ea756..15a2c2283 100644 --- a/radio/src/pulses/modules_constants.h +++ b/radio/src/pulses/modules_constants.h @@ -178,8 +178,9 @@ enum ModuleSubtypeMulti { MODULE_SUBTYPE_MULTI_REALACC, MODULE_SUBTYPE_MULTI_OMP, MODULE_SUBTYPE_MULTI_MLINK, //75 - MODULE_SUBTYPE_MULTI_WFLYRF, - MODULE_SUBTYPE_MULTI_LAST = MODULE_SUBTYPE_MULTI_WFLYRF + MODULE_SUBTYPE_MULTI_WFLY2, + MODULE_SUBTYPE_MULTI_E016HV2, + MODULE_SUBTYPE_MULTI_LAST = MODULE_SUBTYPE_MULTI_E016HV2 }; #define MODULE_SUBTYPE_MULTI_XN297DP 63-3 diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index fedc875c0..e0e6f4bef 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -128,7 +128,7 @@ #define TR_DSM_PROTOCOLS "LP45""DSM2""DSMX" #define LEN_MULTI_PROTOCOLS "\007" -#define TR_MULTI_PROTOCOLS "FlySky\0""Hubsan\0""FrSky\0 ""Hisky\0 ""V2x2\0 ""DSM\0 ""Devo\0 ""YD717\0 ""KN\0 ""SymaX\0 ""SLT\0 ""CX10\0 ""CG023\0 ""Bayang\0""ESky\0 ""MT99XX\0""MJXq\0 ""Shenqi\0""FY326\0 ""Futaba\0""J6 Pro\0""FQ777\0 ""Assan\0 ""Hontai\0""OpenLrs""FlSky2A""Q2x2\0 ""Walkera""Q303\0 ""GW008\0 ""DM002\0 ""Cabell\0""Esky150""H8 3D\0 ""Corona\0""CFlie\0 ""Hitec\0 ""WFly\0 ""Bugs\0 ""BugMini""Traxxas""NCC1701""E01X\0 ""V911S\0 ""GD00X\0 ""V761\0 ""KF606\0 ""Redpine""Potensi""ZSX\0 ""Height\0""Scanner""FrSkyRX""FS2A_RX""HoTT\0 ""FX816\0 ""BayanRX""Pelikan""Tiger\0 ""XK\0 ""XN297DU""FrSkyX2""FrSkyR9""Propel\0""FrSkyL\0""Skyartc""ESky-v2""DSM RX\0""JJRC345""Q90C\0 ""Kyosho\0""RadLink""ExpLRS\0""Realacc""OMP\0 ""M-Link\0""Wfly-RF" +#define TR_MULTI_PROTOCOLS "FlySky\0""Hubsan\0""FrSky\0 ""Hisky\0 ""V2x2\0 ""DSM\0 ""Devo\0 ""YD717\0 ""KN\0 ""SymaX\0 ""SLT\0 ""CX10\0 ""CG023\0 ""Bayang\0""ESky\0 ""MT99XX\0""MJXq\0 ""Shenqi\0""FY326\0 ""Futaba\0""J6 Pro\0""FQ777\0 ""Assan\0 ""Hontai\0""OpenLrs""FlSky2A""Q2x2\0 ""Walkera""Q303\0 ""GW008\0 ""DM002\0 ""Cabell\0""Esky150""H8 3D\0 ""Corona\0""CFlie\0 ""Hitec\0 ""WFly\0 ""Bugs\0 ""BugMini""Traxxas""NCC1701""E01X\0 ""V911S\0 ""GD00X\0 ""V761\0 ""KF606\0 ""Redpine""Potensi""ZSX\0 ""Height\0""Scanner""FrSkyRX""FS2A_RX""HoTT\0 ""FX816\0 ""BayanRX""Pelikan""Tiger\0 ""XK\0 ""XN297DU""FrSkyX2""FrSkyR9""Propel\0""FrSkyL\0""Skyartc""ESky-v2""DSM RX\0""JJRC345""Q90C\0 ""Kyosho\0""RadLink""ExpLRS\0""Realacc""OMP\0 ""M-Link\0""Wfly 2\0""E016Hv2" #define LEN_MULTI_POWER "\005" #define TR_MULTI_POWER "10mW\0""25mW\0""50mW\0""100mW""200mW""300mW""500mW""1W\0 ""2W\0 " From 8bf4090ad343ca7f91c089b92267c9b41ac80da4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 4 Jan 2021 21:25:42 +0100 Subject: [PATCH 15/65] Telemetry driver stubs (#8196) --- radio/src/targets/common/arm/CMakeLists.txt | 6 +-- radio/src/targets/simu/simpgmspace.cpp | 43 ++++++++++++++++++++ radio/src/targets/sky9x/CMakeLists.txt | 6 ++- radio/src/targets/sky9x/board.h | 9 +--- radio/src/targets/sky9x/telemetry_driver.cpp | 18 ++++---- 5 files changed, 61 insertions(+), 21 deletions(-) diff --git a/radio/src/targets/common/arm/CMakeLists.txt b/radio/src/targets/common/arm/CMakeLists.txt index 64e998597..16169ff24 100644 --- a/radio/src/targets/common/arm/CMakeLists.txt +++ b/radio/src/targets/common/arm/CMakeLists.txt @@ -217,12 +217,8 @@ set(FIRMWARE_TARGET_SRC ${FIRMWARE_TARGET_SRC} keys_driver.cpp diskio.cpp -) - -set(TARGET_SRC - ${TARGET_SRC} telemetry_driver.cpp - ) +) set(FIRMWARE_SRC ${FIRMWARE_SRC} diff --git a/radio/src/targets/simu/simpgmspace.cpp b/radio/src/targets/simu/simpgmspace.cpp index fa0122175..8376d786c 100644 --- a/radio/src/targets/simu/simpgmspace.cpp +++ b/radio/src/targets/simu/simpgmspace.cpp @@ -42,6 +42,8 @@ char * main_thread_error = nullptr; bool simu_shutdown = false; bool simu_running = false; +uint32_t telemetryErrors = 0; + #if defined(STM32) GPIO_TypeDef gpioa, gpiob, gpioc, gpiod, gpioe, gpiof, gpiog, gpioh, gpioi, gpioj; TIM_TypeDef tim1, tim2, tim3, tim4, tim5, tim6, tim7, tim8, tim9, tim10; @@ -441,6 +443,47 @@ void sportUpdatePowerInit() { } +void telemetryPortSetDirectionInput() +{ +} + +void telemetryPortSetDirectionOutput() +{ +} + +void rxPdcUsart( void (*pChProcess)(uint8_t x) ) +{ +} + +void telemetryPortInit(uint32_t baudrate, uint8_t mode) +{ +} + +bool telemetryGetByte(uint8_t * byte) +{ + return false; +} + +void telemetryClearFifo() +{ +} + +void telemetryPortInvertedInit(uint32_t baudrate) +{ +} + +void sportSendByte(uint8_t byte) +{ +} + +void sportSendBuffer(const uint8_t * buffer, uint32_t count) +{ +} + +void check_telemetry_exti() +{ +} + void boardInit() { } diff --git a/radio/src/targets/sky9x/CMakeLists.txt b/radio/src/targets/sky9x/CMakeLists.txt index 181b5ab85..842035bde 100644 --- a/radio/src/targets/sky9x/CMakeLists.txt +++ b/radio/src/targets/sky9x/CMakeLists.txt @@ -122,9 +122,13 @@ set(FIRMWARE_TARGET_SRC MEDSdcard.c ) +set(FIRMWARE_TARGET_SRC + ${FIRMWARE_TARGET_SRC} + telemetry_driver.cpp + ) + set(TARGET_SRC ${TARGET_SRC} - telemetry_driver.cpp adc_driver.cpp pulses_driver.cpp audio_driver.cpp diff --git a/radio/src/targets/sky9x/board.h b/radio/src/targets/sky9x/board.h index 0a6aef20d..ca7e23acd 100644 --- a/radio/src/targets/sky9x/board.h +++ b/radio/src/targets/sky9x/board.h @@ -395,13 +395,8 @@ void debugPutc(const char c); // Telemetry driver void telemetryPortInit(uint32_t baudrate, uint8_t mode); -inline void telemetryPortSetDirectionOutput() -{ -} -inline void telemetryPortSetDirectionInput() -{ -} -uint32_t telemetryTransmitPending(); +void telemetryPortSetDirectionOutput(); +void telemetryPortSetDirectionInput(); void telemetryTransmitBuffer(const uint8_t * buffer, uint32_t size); void rxPdcUsart( void (*pChProcess)(uint8_t x) ); void sportSendBuffer(const uint8_t * buffer, uint32_t size); diff --git a/radio/src/targets/sky9x/telemetry_driver.cpp b/radio/src/targets/sky9x/telemetry_driver.cpp index 4875c5d35..8efa83951 100644 --- a/radio/src/targets/sky9x/telemetry_driver.cpp +++ b/radio/src/targets/sky9x/telemetry_driver.cpp @@ -93,10 +93,8 @@ void startPdcUsartReceive() { Usart *pUsart = SECOND_USART; TelemetryInBuffer.outPtr = TelemetryInBuffer.fifo ; -#ifndef SIMU pUsart->US_RPR = (uint32_t)TelemetryInBuffer.fifo ; pUsart->US_RNPR = (uint32_t)TelemetryInBuffer.fifo ; -#endif pUsart->US_RCR = RX_UART_BUFFER_SIZE ; pUsart->US_RNCR = RX_UART_BUFFER_SIZE ; pUsart->US_PTCR = US_PTCR_RXTEN ; @@ -104,7 +102,6 @@ void startPdcUsartReceive() void rxPdcUsart( void (*pChProcess)(uint8_t x) ) { -#if !defined(SIMU) Usart *pUsart = SECOND_USART; uint8_t *ptr ; uint8_t *endPtr ; @@ -132,7 +129,6 @@ void rxPdcUsart( void (*pChProcess)(uint8_t x) ) pUsart->US_RNPR = (uint32_t)TelemetryInBuffer.fifo ; pUsart->US_RNCR = RX_UART_BUFFER_SIZE ; } -#endif } uint32_t txPdcUsart(const uint8_t * buffer, uint32_t size) @@ -140,9 +136,7 @@ uint32_t txPdcUsart(const uint8_t * buffer, uint32_t size) Usart * pUsart = SECOND_USART; if (pUsart->US_TNCR == 0) { -#ifndef SIMU pUsart->US_TNPR = (uint32_t)buffer ; -#endif pUsart->US_TNCR = size ; pUsart->US_PTCR = US_PTCR_TXTEN ; return 1 ; @@ -150,6 +144,7 @@ uint32_t txPdcUsart(const uint8_t * buffer, uint32_t size) return 0 ; } +/* uint32_t telemetryTransmitPending() { Usart *pUsart = SECOND_USART; @@ -164,13 +159,20 @@ uint32_t telemetryTransmitPending() return x ; } +*/ + +void telemetryPortSetDirectionOutput() +{ +} + +void telemetryPortSetDirectionInput() +{ +} void telemetryPortInit(uint32_t baudrate, uint8_t mode) { -#if !defined(SIMU) UART2_Configure(baudrate, Master_frequency, mode); startPdcUsartReceive(); -#endif } void sportSendBuffer(const uint8_t * buffer, uint32_t size) From 73ad753c29694bc610d1533e0d7822c38f3367b9 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 4 Jan 2021 22:42:24 +0100 Subject: [PATCH 16/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index ebbebc904..06d89e24a 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2070,3 +2070,4 @@ Uwe Probst Glendon Satchell Trevor Collins Horst Grashauser +Miguel José Cárdenas Herrera From 9db17c8f25f876bbd02611bf68a0f44b17ebd420 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 5 Jan 2021 07:50:14 +0100 Subject: [PATCH 17/65] Sanitize --- radio/src/telemetry/telemetry.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 7e085488e..47b14d381 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -290,9 +290,7 @@ void telemetryInit(uint8_t protocol) #if defined(LUA) outputTelemetryBuffer.reset(); #endif -#if !defined(SIMU) telemetryPortSetDirectionInput(); -#endif } else if (protocol == PROTOCOL_TELEMETRY_SPEKTRUM) { // Spektrum's own small race RX (SPM4648) uses 125000 8N1, use the same since there is no real standard @@ -327,7 +325,7 @@ void telemetryInit(uint8_t protocol) } #endif -#if defined(AFHDS3) && !defined(SIMU) +#if defined(AFHDS3) else if (protocol == PROTOCOL_TELEMETRY_AFHDS3) { telemetryPortInvertedInit(AFHDS3_BAUDRATE); telemetryPortSetDirectionInput(); From 5157d3720822700de0f516fe5122d1ba36f528fe Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 5 Jan 2021 12:08:10 +0100 Subject: [PATCH 18/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 06d89e24a..fbdface08 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2071,3 +2071,4 @@ Glendon Satchell Trevor Collins Horst Grashauser Miguel José Cárdenas Herrera +Mike Jennings From 30b015d6e581d797b7e7552b9ea8df07dc8b32b3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 5 Jan 2021 19:50:41 +0100 Subject: [PATCH 19/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index fbdface08..cd2e352b0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2072,3 +2072,4 @@ Trevor Collins Horst Grashauser Miguel José Cárdenas Herrera Mike Jennings +José Bernardo From 9ac21b6fa016de7ae198d19913bc24606a6ed81b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 6 Jan 2021 08:49:18 +0100 Subject: [PATCH 20/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index cd2e352b0..462cb77f0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2073,3 +2073,4 @@ Horst Grashauser Miguel José Cárdenas Herrera Mike Jennings José Bernardo +Carlos Dangle From b8c8407be2bd3d08c5964a3464e4d03e174a4910 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 6 Jan 2021 17:38:27 +0100 Subject: [PATCH 21/65] Cosmetics --- .../src/targets/common/arm/stm32/usbd_desc.c | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/usbd_desc.c b/radio/src/targets/common/arm/stm32/usbd_desc.c index 198c78a80..021f6c5d2 100644 --- a/radio/src/targets/common/arm/stm32/usbd_desc.c +++ b/radio/src/targets/common/arm/stm32/usbd_desc.c @@ -54,30 +54,32 @@ * @{ */ -#define USBD_VID 0x0483 +#define USBD_VID_STM 0x0483 // STM Vendor ID +#define USBD_VID_PID_CODES 0x1209 // https://pid.codes #define USBD_LANGID_STRING 0x409 #define USBD_MANUFACTURER_STRING "OpenTX" #define USBD_SERIALNUMBER_FS_STRING "00000000001B" - #if defined(BOOT) #define USBD_MSC_PRODUCT_FS_STRING USB_NAME " Bootloader" #else #define USBD_MSC_PRODUCT_FS_STRING USB_NAME " Mass Storage" #endif +#define USBD_MSC_VID USBD_VID_STM #define USBD_MSC_PID 0x5720 #define USBD_MSC_CONFIGURATION_FS_STRING "MSC Config" #define USBD_MSC_INTERFACE_FS_STRING "MSC Interface" -#define USBD_HID_VID 0x1209 // https://pid.codes +#define USBD_HID_VID USBD_VID_PID_CODES #define USBD_HID_PID 0x4F54 // OpenTX assigned PID #define USBD_HID_PRODUCT_FS_STRING USB_NAME " Joystick" #define USBD_HID_CONFIGURATION_FS_STRING "HID Config" #define USBD_HID_INTERFACE_FS_STRING "HID Interface" -#define USBD_CDC_PID 0x5740 // do not change, this ID is used by the ST USB driver for Windows +#define USBD_CDC_VID USBD_VID_STM +#define USBD_CDC_PID 0x5740 // do not change, this ID is used by the ST USB driver for Windows #define USBD_CDC_PRODUCT_FS_STRING USB_NAME " Serial Port" #define USBD_CDC_CONFIGURATION_FS_STRING "VSP Config" #define USBD_CDC_INTERFACE_FS_STRING "VSP Interface" @@ -127,20 +129,24 @@ __ALIGN_BEGIN uint8_t USBD_StrDesc[USB_MAX_STR_DESC_SIZ] __ALIGN_END ; // modifi * @param length : pointer to data length variable * @retval pointer to descriptor buffer */ -uint8_t * USBD_USR_DeviceDescriptor( uint8_t speed , uint16_t *length) +uint8_t * USBD_USR_DeviceDescriptor( uint8_t speed , uint16_t *length) { - int pid=0; - int vid= USBD_VID; + int vid = 0; + int pid = 0; switch (getSelectedUsbMode()) { case USB_JOYSTICK_MODE: - pid = USBD_HID_PID; vid = USBD_HID_VID; + pid = USBD_HID_PID; break; + case USB_SERIAL_MODE: + vid = USBD_CDC_VID; pid = USBD_CDC_PID; break; + case USB_MASS_STORAGE_MODE: + vid = USBD_MSC_VID; pid = USBD_MSC_PID; break; } From 7e0272dfde436c388c164a4b82c58728b25a3b8e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 6 Jan 2021 17:42:00 +0100 Subject: [PATCH 22/65] Cosmetics --- radio/src/targets/common/arm/stm32/usbd_desc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/usbd_desc.c b/radio/src/targets/common/arm/stm32/usbd_desc.c index 021f6c5d2..1efa185da 100644 --- a/radio/src/targets/common/arm/stm32/usbd_desc.c +++ b/radio/src/targets/common/arm/stm32/usbd_desc.c @@ -131,8 +131,8 @@ __ALIGN_BEGIN uint8_t USBD_StrDesc[USB_MAX_STR_DESC_SIZ] __ALIGN_END ; // modifi */ uint8_t * USBD_USR_DeviceDescriptor( uint8_t speed , uint16_t *length) { - int vid = 0; - int pid = 0; + int vid; + int pid; switch (getSelectedUsbMode()) { case USB_JOYSTICK_MODE: @@ -149,6 +149,10 @@ uint8_t * USBD_USR_DeviceDescriptor( uint8_t speed , uint16_t *length) vid = USBD_MSC_VID; pid = USBD_MSC_PID; break; + + default: + vid = 0; + pid = 0; } /* USB Standard Device Descriptor */ From 00f462d8b57c97d29f3ad5475a10af10720a9fe3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 6 Jan 2021 17:42:33 +0100 Subject: [PATCH 23/65] Cosmetics --- radio/src/targets/common/arm/stm32/usbd_desc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/usbd_desc.c b/radio/src/targets/common/arm/stm32/usbd_desc.c index 1efa185da..c43359c8a 100644 --- a/radio/src/targets/common/arm/stm32/usbd_desc.c +++ b/radio/src/targets/common/arm/stm32/usbd_desc.c @@ -131,8 +131,7 @@ __ALIGN_BEGIN uint8_t USBD_StrDesc[USB_MAX_STR_DESC_SIZ] __ALIGN_END ; // modifi */ uint8_t * USBD_USR_DeviceDescriptor( uint8_t speed , uint16_t *length) { - int vid; - int pid; + int vid, pid; switch (getSelectedUsbMode()) { case USB_JOYSTICK_MODE: From 61ed2f39a56b860e0bbdce58a6adbf6be366bc77 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 6 Jan 2021 19:18:57 +0100 Subject: [PATCH 24/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 462cb77f0..08cadc3d8 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2074,3 +2074,4 @@ Miguel José Cárdenas Herrera Mike Jennings José Bernardo Carlos Dangle +Gordon Evans From c4b89aca4d1735f0989764b3500356f1978f4d22 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 6 Jan 2021 19:26:00 +0100 Subject: [PATCH 25/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 08cadc3d8..e6b70cee0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2075,3 +2075,4 @@ Mike Jennings José Bernardo Carlos Dangle Gordon Evans +Pablo Peinado Abad From 4ef7ef4217d1c379ea240ba8b0c6d049e433bf97 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 7 Jan 2021 09:28:32 +0100 Subject: [PATCH 26/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index e6b70cee0..11affcfd7 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2076,3 +2076,4 @@ José Bernardo Carlos Dangle Gordon Evans Pablo Peinado Abad +Carl Payne From 1cacea505dd5b6a081d8464ed6951822b35c7368 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 7 Jan 2021 14:22:49 +0100 Subject: [PATCH 27/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 11affcfd7..d0bae7522 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2077,3 +2077,4 @@ Carlos Dangle Gordon Evans Pablo Peinado Abad Carl Payne +Adam Sosnowski From ce43bfdbd323c49f2a425793d932b9359de08eba Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 7 Jan 2021 15:58:39 +0100 Subject: [PATCH 28/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index d0bae7522..a74423f72 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2078,3 +2078,4 @@ Gordon Evans Pablo Peinado Abad Carl Payne Adam Sosnowski +Гасак Сергей From 7e07b6d5de4fdda54a409435d35cc7fa5eae66c5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 7 Jan 2021 22:01:45 +0100 Subject: [PATCH 29/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index a74423f72..11971219f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2079,3 +2079,4 @@ Pablo Peinado Abad Carl Payne Adam Sosnowski Гасак Сергей +SaltCtyRacingFPV From b7594014f77f7a93de66cdfbbaf25237cd7075a9 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 8 Jan 2021 08:48:51 +0100 Subject: [PATCH 30/65] Fix x7 Access switch number --- companion/src/firmwares/boards.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 35f7a0bab..82e645d05 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -191,7 +191,20 @@ SwitchInfo Boards::getSwitchInfo(Board::Type board, int index) if (index < DIM(switches)) return switches[index]; } - else if (IS_TARANIS_X7(board)) { + else if (board == BOARD_TARANIS_X7_ACCESS) { + const Board::SwitchInfo switches[] = { + {SWITCH_3POS, "SA"}, + {SWITCH_3POS, "SB"}, + {SWITCH_3POS, "SC"}, + {SWITCH_3POS, "SD"}, + {SWITCH_2POS, "SF"}, + {SWITCH_TOGGLE, "SH"}, + {SWITCH_2POS, "SI"} + }; + if (index < DIM(switches)) + return switches[index]; + } + else if (board == BOARD_TARANIS_X7) { const Board::SwitchInfo switches[] = { {SWITCH_3POS, "SA"}, {SWITCH_3POS, "SB"}, @@ -350,7 +363,9 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 5; else if (board == Board::BOARD_TARANIS_X9LITES) return 7; - else if (IS_TARANIS_X7(board)) + else if (board == BOARD_TARANIS_X7_ACCESS) + return 7; + else if (board == BOARD_TARANIS_X7) return 8; else if (IS_FAMILY_T12(board)) return 8; From f2ed651f77df43aa528ac44b25bfe57707b1b152 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 8 Jan 2021 09:03:40 +0100 Subject: [PATCH 31/65] Fix --- companion/src/firmwares/boards.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 82e645d05..bb666e842 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -363,9 +363,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 5; else if (board == Board::BOARD_TARANIS_X9LITES) return 7; - else if (board == BOARD_TARANIS_X7_ACCESS) - return 7; - else if (board == BOARD_TARANIS_X7) + else if (IS_TARANIS_X7(board)) return 8; else if (IS_FAMILY_T12(board)) return 8; From 687fba6aded2b40b7f798bace0dcb56962a1db68 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 8 Jan 2021 09:41:45 +0100 Subject: [PATCH 32/65] Fix --- companion/src/firmwares/boards.cpp | 4 +++- companion/src/firmwares/opentx/opentxeeprom.cpp | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index bb666e842..82e645d05 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -363,7 +363,9 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 5; else if (board == Board::BOARD_TARANIS_X9LITES) return 7; - else if (IS_TARANIS_X7(board)) + else if (board == BOARD_TARANIS_X7_ACCESS) + return 7; + else if (board == BOARD_TARANIS_X7) return 8; else if (IS_FAMILY_T12(board)) return 8; diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index f6adcc563..264a3ad6a 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -48,6 +48,9 @@ inline int MAX_SWITCHES(Board::Type board, int version) if (IS_FAMILY_T12(board)) return 8; + if (IS_TARANIS_X7(board)) + return 8; + return Boards::getCapability(board, Board::Switches); } From 905886774fd07d78330ec308a68da43460f68b2e Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 8 Jan 2021 19:55:57 +1100 Subject: [PATCH 33/65] Major refactoring continued --- companion/src/datamodels/CMakeLists.txt | 8 +- ...mdatamodels.cpp => compounditemmodels.cpp} | 301 ++++++++++++------ ...witemdatamodels.h => compounditemmodels.h} | 191 ++++++----- ...lteredmodel.cpp => filtereditemmodels.cpp} | 121 +++++-- ...emfilteredmodel.h => filtereditemmodels.h} | 51 +-- companion/src/datamodels/itemmodelfactory.cpp | 8 +- companion/src/datamodels/itemmodelfactory.h | 6 +- companion/src/firmwares/curvereference.cpp | 51 +-- companion/src/firmwares/curvereference.h | 22 +- companion/src/firmwares/sensordata.cpp | 164 +++++++++- companion/src/firmwares/sensordata.h | 19 +- companion/src/generaledit/generaledit.cpp | 12 +- companion/src/generaledit/generaledit.h | 4 +- companion/src/modeledit/channels.cpp | 15 +- companion/src/modeledit/channels.h | 7 +- companion/src/modeledit/curves.cpp | 6 +- companion/src/modeledit/curves.h | 6 +- companion/src/modeledit/customfunctions.cpp | 26 +- companion/src/modeledit/customfunctions.h | 6 +- companion/src/modeledit/expodialog.cpp | 28 +- companion/src/modeledit/expodialog.h | 10 +- companion/src/modeledit/flightmodes.cpp | 26 +- companion/src/modeledit/flightmodes.h | 8 +- companion/src/modeledit/heli.cpp | 13 +- companion/src/modeledit/heli.h | 5 +- companion/src/modeledit/inputs.cpp | 45 +-- companion/src/modeledit/inputs.h | 14 +- companion/src/modeledit/logicalswitches.cpp | 20 +- companion/src/modeledit/logicalswitches.h | 7 +- companion/src/modeledit/mixerdialog.cpp | 23 +- companion/src/modeledit/mixerdialog.h | 9 +- companion/src/modeledit/mixes.cpp | 43 +-- companion/src/modeledit/mixes.h | 14 +- companion/src/modeledit/modeledit.cpp | 24 +- companion/src/modeledit/modeledit.h | 4 +- companion/src/modeledit/setup.cpp | 29 +- companion/src/modeledit/setup.h | 10 +- companion/src/modeledit/telemetry.cpp | 134 +++++--- companion/src/modeledit/telemetry.h | 16 +- 39 files changed, 978 insertions(+), 528 deletions(-) rename companion/src/datamodels/{rawitemdatamodels.cpp => compounditemmodels.cpp} (70%) rename companion/src/datamodels/{rawitemdatamodels.h => compounditemmodels.h} (62%) rename companion/src/datamodels/{rawitemfilteredmodel.cpp => filtereditemmodels.cpp} (51%) rename companion/src/datamodels/{rawitemfilteredmodel.h => filtereditemmodels.h} (61%) diff --git a/companion/src/datamodels/CMakeLists.txt b/companion/src/datamodels/CMakeLists.txt index beb7d4d2d..446f161b3 100644 --- a/companion/src/datamodels/CMakeLists.txt +++ b/companion/src/datamodels/CMakeLists.txt @@ -1,12 +1,12 @@ set(datamodels_SRCS - rawitemdatamodels.cpp - rawitemfilteredmodel.cpp + compounditemmodels.cpp + filtereditemmodels.cpp ) set(datamodels_HDRS - rawitemdatamodels.h - rawitemfilteredmodel.h + compounditemmodels.h + filtereditemmodels.h ) qt5_wrap_cpp(datamodels_SRCS ${datamodels_HDRS}) diff --git a/companion/src/datamodels/rawitemdatamodels.cpp b/companion/src/datamodels/compounditemmodels.cpp similarity index 70% rename from companion/src/datamodels/rawitemdatamodels.cpp rename to companion/src/datamodels/compounditemmodels.cpp index 59b1542d5..039e90244 100644 --- a/companion/src/datamodels/rawitemdatamodels.cpp +++ b/companion/src/datamodels/compounditemmodels.cpp @@ -18,30 +18,81 @@ * GNU General Public License for more details. */ -#include "rawitemdatamodels.h" +#include "compounditemmodels.h" #include "generalsettings.h" #include "eeprominterface.h" #include "modeldata.h" #include "adjustmentreference.h" +// static +QString AbstractItemModel::idToString(const int value) +{ + switch (value) { + case IMID_Unknown: + return "Unknown"; + case IMID_RawSource: + return "RawSource"; + case IMID_RawSwitch: + return "RawSwitch"; + case IMID_Curve: + return "Curve"; + case IMID_GVarRef: + return "GVarRef"; + case IMID_ThrSource: + return "ThrSource"; + case IMID_CustomFuncAction: + return "CustomFuncAction"; + case IMID_CustomFuncResetParam: + return "CustomFuncResetParam"; + case IMID_TeleSource: + return "TeleSource"; + case IMID_CurveRefType: + return "CurveRefType"; + case IMID_CurveRefFunc: + return "CurveRefFunc"; + default: + return "Custom"; + } +} + // static void AbstractItemModel::dumpItemModelContents(AbstractItemModel * itemModel) { if (itemModel) { - qDebug() << "modelid:" << itemModel->getId(); + qDebug() << "id:" << itemModel->getId() << "name:" << itemModel->getName(); + for (int i = 0; i < itemModel->rowCount(); ++i) { qDebug() << "row:" << i << "text:" << itemModel->data(itemModel->index(i, 0), Qt::DisplayRole).toString() - << "id:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemIdRole).toInt() - << "avail:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IsAvailableRole).toBool() - << "flags:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemFlagsRole).toInt() - << "type:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemTypeRole).toInt(); + << "id:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Id).toInt() + << "avail:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Available).toBool() + << "flags:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Flags).toInt() + << "type:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Type).toInt(); } } else - qDebug() << "Error: model did not cast"; + qDebug() << "Error: model not of class AbstractItemModel"; } +// +// AbstractStaticItemModel +// + +void AbstractStaticItemModel::loadItemList() +{ + foreach (const ListItem *item, itemList) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setText(item->text); + modelItem->setData(item->id, IMDR_Id); + modelItem->setData(item->type, IMDR_Type); + modelItem->setData(item->flags, IMDR_Flags); + modelItem->setData(item->isAvailable, IMDR_Available); + appendRow(modelItem); + } + + itemList.clear(); +}; + // // RawSourceItemModel // @@ -50,8 +101,8 @@ RawSourceItemModel::RawSourceItemModel(const GeneralSettings * const generalSett Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(RawSourceId); - setUpdateMask(AllTriggers &~ (CurvesUpdated | ScriptsUpdated)); + setId(IMID_RawSource); + setUpdateMask(IMUE_All &~ (IMUE_Curves | IMUE_Scripts)); addItems(SOURCE_TYPE_NONE, RawSource::NoneGroup, 1); for (int i = 0; i < firmware->getCapability(LuaScripts); i++) @@ -74,7 +125,7 @@ RawSourceItemModel::RawSourceItemModel(const GeneralSettings * const generalSett void RawSourceItemModel::setDynamicItemData(QStandardItem * item, const RawSource & src) const { item->setText(src.toString(modelData, generalSettings, boardType)); - item->setData(src.isAvailable(modelData, generalSettings, boardType), IsAvailableRole); + item->setData(src.isAvailable(modelData, generalSettings, boardType), IMDR_Available); } void RawSourceItemModel::addItems(const RawSourceType & type, const int group, const int count, const int start) @@ -82,21 +133,21 @@ void RawSourceItemModel::addItems(const RawSourceType & type, const int group, c for (int i = start; i < start + count; i++) { const RawSource src = RawSource(type, i); QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(src.toValue(), ItemIdRole); - modelItem->setData(type, ItemTypeRole); - modelItem->setData(group, ItemFlagsRole); + modelItem->setData(src.toValue(), IMDR_Id); + modelItem->setData(type, IMDR_Type); + modelItem->setData(group, IMDR_Flags); setDynamicItemData(modelItem, src); appendRow(modelItem); } } -void RawSourceItemModel::update(const UpdateTrigger trigger) +void RawSourceItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), RawSource(item(i)->data(ItemIdRole).toInt())); + setDynamicItemData(item(i), RawSource(item(i)->data(IMDR_Id).toInt())); emit updateComplete(); } @@ -110,8 +161,8 @@ RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSett Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(RawSwitchId); - setUpdateMask(FlightModesUpdated | LogicalSwitchesUpdated | TeleSensorsUpdated); + setId(IMID_RawSwitch); + setUpdateMask(IMUE_FlightModes | IMUE_LogicalSwitches | IMUE_TeleSensors); // Descending switch direction: NOT (!) switches addItems(SWITCH_TYPE_ACT, -1); @@ -143,7 +194,7 @@ RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSett void RawSwitchItemModel::setDynamicItemData(QStandardItem * item, const RawSwitch & rsw) const { item->setText(rsw.toString(boardType, generalSettings, modelData)); - item->setData(rsw.isAvailable(modelData, generalSettings, boardType), IsAvailableRole); + item->setData(rsw.isAvailable(modelData, generalSettings, boardType), IMDR_Available); } void RawSwitchItemModel::addItems(const RawSwitchType & type, int count) @@ -186,21 +237,21 @@ void RawSwitchItemModel::addItems(const RawSwitchType & type, int count) for ( ; i < count; ++i) { RawSwitch rs(type, i + rawIdxAdj); QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(rs.toValue(), ItemIdRole); - modelItem->setData(type, ItemTypeRole); - modelItem->setData(context, ItemFlagsRole); + modelItem->setData(rs.toValue(), IMDR_Id); + modelItem->setData(type, IMDR_Type); + modelItem->setData(context, IMDR_Flags); setDynamicItemData(modelItem, rs); appendRow(modelItem); } } -void RawSwitchItemModel::update(const UpdateTrigger trigger) +void RawSwitchItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), RawSwitch(item(i)->data(ItemIdRole).toInt())); + setDynamicItemData(item(i), RawSwitch(item(i)->data(IMDR_Id).toInt())); emit updateComplete(); } @@ -214,22 +265,19 @@ CurveItemModel::CurveItemModel(const GeneralSettings * const generalSettings, co Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(CurveId); + setId(IMID_Curve); if (!modelData) return; - setUpdateMask(CurvesUpdated); + setUpdateMask(IMUE_Curves); const int count = firmware->getCapability(NumCurves); for (int i = -count ; i <= count; ++i) { QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(i, ItemIdRole); - int flags = i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup; - if (i == 0) - flags |= DataGroups::NoneGroup; - modelItem->setData(flags, ItemFlagsRole); + modelItem->setData(i, IMDR_Id); + modelItem->setData(i < 0 ? IMDG_Negative : i > 0 ? IMDG_Positive : IMDG_None, IMDR_Flags); setDynamicItemData(modelItem, i); appendRow(modelItem); } @@ -239,16 +287,16 @@ void CurveItemModel::setDynamicItemData(QStandardItem * item, const int value) c { CurveReference cr = CurveReference(CurveReference::CURVE_REF_CUSTOM, value); item->setText(cr.toString(modelData, false)); - item->setData(cr.isAvailable(), IsAvailableRole); + item->setData(cr.isAvailable(), IMDR_Available); } -void CurveItemModel::update(const UpdateTrigger trigger) +void CurveItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + setDynamicItemData(item(i), item(i)->data(IMDR_Id).toInt()); emit updateComplete(); } @@ -262,12 +310,12 @@ GVarReferenceItemModel::GVarReferenceItemModel(const GeneralSettings * const gen Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(GVarRefId); + setId(IMID_GVarRef); if (!modelData) return; - setUpdateMask(GVarsUpdated | FlightModesUpdated | LogicalSwitchesUpdated); + setUpdateMask(IMUE_GVars | IMUE_FlightModes | IMUE_LogicalSwitches); const int count = firmware->getCapability(Gvars); @@ -284,8 +332,8 @@ void GVarReferenceItemModel::addItems(int count) for ( ; i < count; ++i) { QStandardItem * modelItem = new QStandardItem(); AdjustmentReference ar(AdjustmentReference::ADJUST_REF_GVAR, i); - modelItem->setData(ar.toValue(), ItemIdRole); - modelItem->setData(i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup, ItemFlagsRole); + modelItem->setData(ar.toValue(), IMDR_Id); + modelItem->setData(i < 0 ? IMDG_Negative : i > 0 ? IMDG_Positive : IMDG_None, IMDR_Flags); setDynamicItemData(modelItem, ar); appendRow(modelItem); } @@ -294,16 +342,16 @@ void GVarReferenceItemModel::addItems(int count) void GVarReferenceItemModel::setDynamicItemData(QStandardItem * item, const AdjustmentReference & ar) const { item->setText(ar.toString(modelData, false)); - item->setData(ar.isAvailable(), IsAvailableRole); + item->setData(ar.isAvailable(), IMDR_Available); } -void GVarReferenceItemModel::update(const UpdateTrigger trigger) +void GVarReferenceItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), AdjustmentReference(item(i)->data(ItemIdRole).toInt())); + setDynamicItemData(item(i), AdjustmentReference(item(i)->data(IMDR_Id).toInt())); emit updateComplete(); } @@ -317,16 +365,16 @@ ThrottleSourceItemModel::ThrottleSourceItemModel(const GeneralSettings * const g Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(ThrSourceId); + setId(IMID_ThrSource); if (!modelData) return; - setUpdateMask(TimersUpdated | InputsUpdated | TeleSensorsUpdated); + setUpdateMask(IMUE_Timers | IMUE_Inputs | IMUE_TeleSensors); for (int i = 0; i < modelData->thrTraceSrcCount(); i++) { QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(i, ItemIdRole); + modelItem->setData(i, IMDR_Id); setDynamicItemData(modelItem, i); appendRow(modelItem); } @@ -335,16 +383,16 @@ ThrottleSourceItemModel::ThrottleSourceItemModel(const GeneralSettings * const g void ThrottleSourceItemModel::setDynamicItemData(QStandardItem * item, const int value) const { item->setText(modelData->thrTraceSrcToString(value)); - item->setData(modelData->isThrTraceSrcAvailable(generalSettings, value), IsAvailableRole); + item->setData(modelData->isThrTraceSrcAvailable(generalSettings, value), IMDR_Available); } -void ThrottleSourceItemModel::update(const UpdateTrigger trigger) +void ThrottleSourceItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + setDynamicItemData(item(i), item(i)->data(IMDR_Id).toInt()); emit updateComplete(); } @@ -358,13 +406,13 @@ CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * con Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(CustomFuncActionId); - setUpdateMask(AllTriggers &~ (CurvesUpdated | ScriptsUpdated)); + setId(IMID_CustomFuncAction); + setUpdateMask(IMUE_All &~ (IMUE_Curves | IMUE_Scripts)); for (int i = 0; i < AssignFunc::FuncCount; i++) { QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(i, ItemIdRole); - modelItem->setData(CustomFunctionData::funcContext(i), ItemFlagsRole); + modelItem->setData(i, IMDR_Id); + modelItem->setData(CustomFunctionData::funcContext(i), IMDR_Flags); setDynamicItemData(modelItem, i); appendRow(modelItem); } @@ -373,16 +421,16 @@ CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * con void CustomFuncActionItemModel::setDynamicItemData(QStandardItem * item, const int value) const { item->setText(CustomFunctionData(AssignFunc(value)).funcToString(modelData)); - item->setData(CustomFunctionData::isFuncAvailable(value), IsAvailableRole); + item->setData(CustomFunctionData::isFuncAvailable(value), IMDR_Available); } -void CustomFuncActionItemModel::update(const UpdateTrigger trigger) +void CustomFuncActionItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + setDynamicItemData(item(i), item(i)->data(IMDR_Id).toInt()); emit updateComplete(); } @@ -396,12 +444,12 @@ CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettin Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(CustomFuncResetParamId); - setUpdateMask(TeleSensorsUpdated); + setId(IMID_CustomFuncResetParam); + setUpdateMask(IMUE_TeleSensors); for (int i = 0; i < CustomFunctionData::resetParamCount(modelData); i++) { QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(i, ItemIdRole); + modelItem->setData(i, IMDR_Id); setDynamicItemData(modelItem, i); appendRow(modelItem); } @@ -412,16 +460,16 @@ void CustomFuncResetParamItemModel::setDynamicItemData(QStandardItem * item, con CustomFunctionData cfd = CustomFunctionData(AssignFunc::FuncReset); cfd.param = value; item->setText(cfd.paramToString(modelData)); - item->setData(CustomFunctionData::isResetParamAvailable(modelData, value), IsAvailableRole); + item->setData(CustomFunctionData::isResetParamAvailable(modelData, value), IMDR_Available); } -void CustomFuncResetParamItemModel::update(const UpdateTrigger trigger) +void CustomFuncResetParamItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + setDynamicItemData(item(i), item(i)->data(IMDR_Id).toInt()); emit updateComplete(); } @@ -435,21 +483,18 @@ TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(TeleSourceId); + setId(IMID_TeleSource); if (!modelData) return; - setUpdateMask(TeleSensorsUpdated); + setUpdateMask(IMUE_TeleSensors); const int count = firmware->getCapability(Sensors); for (int i = -count; i <= count; ++i) { QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(i, ItemIdRole); - int flags = i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup; - if (i == 0) - flags |= DataGroups::NoneGroup; - modelItem->setData(flags, ItemFlagsRole); + modelItem->setData(i, IMDR_Id); + modelItem->setData(i < 0 ? IMDG_Negative : i > 0 ? IMDG_Positive : IMDG_None, IMDR_Flags); setDynamicItemData(modelItem, i); appendRow(modelItem); } @@ -458,16 +503,16 @@ TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const void TelemetrySourceItemModel::setDynamicItemData(QStandardItem * item, const int value) const { item->setText(SensorData::sourceToString(modelData, value)); - item->setData(SensorData::isSourceAvailable(modelData, value), IsAvailableRole); + item->setData(SensorData::isSourceAvailable(modelData, value), IMDR_Available); } -void TelemetrySourceItemModel::update(const UpdateTrigger trigger) +void TelemetrySourceItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) { - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + setDynamicItemData(item(i), item(i)->data(IMDR_Id).toInt()); } emit updateComplete(); @@ -482,13 +527,13 @@ CurveRefTypeItemModel::CurveRefTypeItemModel(const GeneralSettings * const gener Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractStaticItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(CurveRefTypeId); + setId(IMID_CurveRefType); for (int i = 0; i <= CurveReference::MAX_CURVE_REF_TYPE; i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setText(CurveReference::typeToString((CurveReference::CurveRefType)i)); - modelItem->setData(i, ItemIdRole); - modelItem->setData(CurveReference::isTypeAvailable((CurveReference::CurveRefType)i), IsAvailableRole); + modelItem->setData(i, IMDR_Id); + modelItem->setData(CurveReference::isTypeAvailable((CurveReference::CurveRefType)i), IMDR_Available); appendRow(modelItem); } } @@ -501,22 +546,43 @@ CurveRefFuncItemModel::CurveRefFuncItemModel(const GeneralSettings * const gener Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractStaticItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(CurveRefFuncId); + setId(IMID_CurveRefFunc); for (int i = 1; i <= CurveReference::functionCount(); i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setText(CurveReference::functionToString(i)); - modelItem->setData(i, ItemIdRole); - modelItem->setData(CurveReference::isFunctionAvailable(i), IsAvailableRole); + modelItem->setData(i, IMDR_Id); + modelItem->setData(CurveReference::isFunctionAvailable(i), IMDR_Available); appendRow(modelItem); } } // -// ItemModelsFactory +// PrecisionItemModel // -ItemModelsFactory::ItemModelsFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData) : +PrecisionItemModel::PrecisionItemModel(const int minDecimals, const int maxDecimals, const QString suffix, const bool placeholders) : + AbstractStaticItemModel() +{ + for (int i = minDecimals, j = maxDecimals; j >= 0; i++, j--) { + QString str = QString("0.%1").arg(QString(i, '0')); + if (placeholders) + str.append(QString(j, '_')); + if (!suffix.isEmpty()) + str.append(QString(" %1").arg(suffix)); + QStandardItem * modelItem = new QStandardItem(); + modelItem->setText(str); + modelItem->setData(i, IMDR_Id); + modelItem->setData(true, IMDR_Available); + appendRow(modelItem); + } +} + +// +// CompoundItemModelFactory +// + +CompoundItemModelFactory::CompoundItemModelFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData) : generalSettings(generalSettings), modelData(modelData) { @@ -525,43 +591,43 @@ ItemModelsFactory::ItemModelsFactory(const GeneralSettings * const generalSettin boardType = getCurrentBoard(); } -ItemModelsFactory::~ItemModelsFactory() +CompoundItemModelFactory::~CompoundItemModelFactory() { unregisterItemModels(); delete board; } -void ItemModelsFactory::addItemModel(const AbstractItemModel::ItemModelId id) +void CompoundItemModelFactory::addItemModel(const int id) { switch (id) { - case AbstractItemModel::RawSourceId: + case AbstractItemModel::IMID_RawSource: registerItemModel(new RawSourceItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::RawSwitchId: + case AbstractItemModel::IMID_RawSwitch: registerItemModel(new RawSwitchItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::CurveId: + case AbstractItemModel::IMID_Curve: registerItemModel(new CurveItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::GVarRefId: + case AbstractItemModel::IMID_GVarRef: registerItemModel(new GVarReferenceItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::ThrSourceId: + case AbstractItemModel::IMID_ThrSource: registerItemModel(new ThrottleSourceItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::CustomFuncActionId: + case AbstractItemModel::IMID_CustomFuncAction: registerItemModel(new CustomFuncActionItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::CustomFuncResetParamId: + case AbstractItemModel::IMID_CustomFuncResetParam: registerItemModel(new CustomFuncResetParamItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::TeleSourceId: + case AbstractItemModel::IMID_TeleSource: registerItemModel(new TelemetrySourceItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::CurveRefTypeId: + case AbstractItemModel::IMID_CurveRefType: registerItemModel(new CurveRefTypeItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::CurveRefFuncId: + case AbstractItemModel::IMID_CurveRefFunc: registerItemModel(new CurveRefFuncItemModel(generalSettings, modelData, firmware, board, boardType)); break; default: @@ -570,28 +636,35 @@ void ItemModelsFactory::addItemModel(const AbstractItemModel::ItemModelId id) } } -void ItemModelsFactory::registerItemModel(AbstractItemModel * itemModel) +int CompoundItemModelFactory::registerItemModel(AbstractItemModel * itemModel) { - if (itemModel && !getItemModel(itemModel->getId())) { - registeredItemModels.push_back(itemModel); + if (itemModel) { + if (!isItemModelRegistered(itemModel->getId())) { + setSourceId(itemModel); + registeredItemModels.append(itemModel); + return itemModel->getId(); + } + else + return itemModel->getId(); } + return -1; } -void ItemModelsFactory::unregisterItemModels() +void CompoundItemModelFactory::unregisterItemModels() { foreach (AbstractItemModel *itemModel, registeredItemModels) { delete itemModel; } } -void ItemModelsFactory::unregisterItemModel(const AbstractItemModel::ItemModelId id) +void CompoundItemModelFactory::unregisterItemModel(const int id) { AbstractItemModel * itemModel = getItemModel(id); if (itemModel) delete itemModel; } -AbstractItemModel * ItemModelsFactory::getItemModel(const AbstractItemModel::ItemModelId id) const +AbstractItemModel * CompoundItemModelFactory::getItemModel(const int id) const { foreach (AbstractItemModel * itemModel, registeredItemModels) { if (itemModel->getId() == id) @@ -601,16 +674,32 @@ AbstractItemModel * ItemModelsFactory::getItemModel(const AbstractItemModel::Ite return nullptr; } -void ItemModelsFactory::update(const AbstractItemModel::UpdateTrigger trigger) +bool CompoundItemModelFactory::isItemModelRegistered(const int id) const { foreach (AbstractItemModel * itemModel, registeredItemModels) { - itemModel->update(trigger); + if (itemModel->getId() == id) + return true; + } + + return false; +} + +void CompoundItemModelFactory::update(const int event) +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + itemModel->update(event); } } -void ItemModelsFactory::dumpAllItemModelContents() const +void CompoundItemModelFactory::dumpAllItemModelContents() const { foreach (AbstractItemModel * itemModel, registeredItemModels) { AbstractItemModel::dumpItemModelContents(itemModel); } } + +void CompoundItemModelFactory::setSourceId(AbstractItemModel * itemModel) +{ + if (itemModel && itemModel->getId() == AbstractItemModel::IMID_Unknown) + itemModel->setId(AbstractItemModel::IMID_ReservedCount + registeredItemModels.count()); +} diff --git a/companion/src/datamodels/rawitemdatamodels.h b/companion/src/datamodels/compounditemmodels.h similarity index 62% rename from companion/src/datamodels/rawitemdatamodels.h rename to companion/src/datamodels/compounditemmodels.h index 89f24acdc..de32fc902 100644 --- a/companion/src/datamodels/rawitemdatamodels.h +++ b/companion/src/datamodels/compounditemmodels.h @@ -18,8 +18,8 @@ * GNU General Public License for more details. */ -#ifndef RAWITEMDATAMODELS_H -#define RAWITEMDATAMODELS_H +#ifndef COMPOUNDITEMMODELS_H +#define COMPOUNDITEMMODELS_H #include "rawsource.h" #include "rawswitch.h" @@ -35,51 +35,53 @@ class AbstractItemModel: public QStandardItemModel Q_OBJECT public: enum ItemModelId { - UnknownId, - RawSourceId, - RawSwitchId, - CurveId, - GVarRefId, - ThrSourceId, - CustomFuncActionId, - CustomFuncResetParamId, - TeleSourceId, - CurveRefTypeId, - CurveRefFuncId + IMID_Unknown, + IMID_RawSource, + IMID_RawSwitch, + IMID_Curve, + IMID_GVarRef, + IMID_ThrSource, + IMID_CustomFuncAction, + IMID_CustomFuncResetParam, + IMID_TeleSource, + IMID_CurveRefType, + IMID_CurveRefFunc, + IMID_ReservedCount, + IMID_Custom }; Q_ENUM(ItemModelId) - enum DataRoles { - ItemIdRole = Qt::UserRole, - ItemTypeRole, - ItemFlagsRole, - IsAvailableRole + enum ItemModelDataRoles { + IMDR_Id = Qt::UserRole, + IMDR_Type, + IMDR_Flags, + IMDR_Available }; - Q_ENUM(DataRoles) + Q_ENUM(ItemModelDataRoles) - enum DataGroups { - NoneGroup = 0x01, - NegativeGroup = 0x02, - PositiveGroup = 0x04 + enum ItemModelDataGroups { + IMDG_None = 0x01, + IMDG_Negative = 0x02, + IMDG_Positive = 0x04 }; - Q_ENUM(DataGroups) + Q_ENUM(ItemModelDataGroups) - enum UpdateTrigger { - SystemRefresh = 1 << 0, - ChannelsUpdated = 1 << 1, - CurvesUpdated = 1 << 2, - FlightModesUpdated = 1 << 3, - GVarsUpdated = 1 << 4, - InputsUpdated = 1 << 5, - LogicalSwitchesUpdated = 1 << 6, - ScriptsUpdated = 1 << 7, - TeleSensorsUpdated = 1 << 8, - TimersUpdated = 1 << 9, - AllTriggers = SystemRefresh | ChannelsUpdated | CurvesUpdated | FlightModesUpdated | GVarsUpdated | InputsUpdated | - LogicalSwitchesUpdated | ScriptsUpdated | TeleSensorsUpdated | TimersUpdated, - NoTriggers = 0 + enum ItemModelUpdateEvent { + IMUE_None = 0, + IMUE_SystemRefresh = 1 << 0, + IMUE_Channels = 1 << 1, + IMUE_Curves = 1 << 2, + IMUE_FlightModes = 1 << 3, + IMUE_GVars = 1 << 4, + IMUE_Inputs = 1 << 5, + IMUE_LogicalSwitches = 1 << 6, + IMUE_Scripts = 1 << 7, + IMUE_TeleSensors = 1 << 8, + IMUE_Timers = 1 << 9, + IMUE_All = IMUE_SystemRefresh | IMUE_Channels | IMUE_Curves | IMUE_FlightModes | IMUE_GVars | IMUE_Inputs | + IMUE_LogicalSwitches | IMUE_Scripts | IMUE_TeleSensors | IMUE_Timers }; - Q_ENUM(UpdateTrigger) + Q_ENUM(ItemModelUpdateEvent) explicit AbstractItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, Firmware * firmware, const Boards * const board, const Board::Type boardType) : @@ -88,24 +90,30 @@ class AbstractItemModel: public QStandardItemModel modelData(modelData), firmware(firmware), board(board), - boardType(boardType) - {} + boardType(boardType), + m_id(IMID_Unknown), + m_name(""), + m_updateMask(IMUE_None) + {} - virtual ~AbstractItemModel() {}; - - void setId(ItemModelId id) { m_id = id; } - ItemModelId getId () const { return m_id; } + virtual ~AbstractItemModel() {} + void setId(int id) { m_id = id; } + int getId () const { return m_id; } + bool isReservedModelId() const { return m_id > IMID_Unknown && m_id < IMID_ReservedCount; } + void setName(QString name) { m_name = name; } + QString getName() const { return isReservedModelId() ? idToString(m_id) : m_name; } void setUpdateMask(const int mask) { m_updateMask = mask; } int getUpdateMask() const { return m_updateMask; } - inline bool doUpdate(const UpdateTrigger trigger) const { return m_updateMask & (int)trigger; } - AbstractItemModel * getItemModel(const ItemModelId id) const; + inline bool doUpdate(const int event) const { return m_updateMask & event; } + + AbstractItemModel * getItemModel(const int id) const; static void dumpItemModelContents(AbstractItemModel * itemModel); public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) = 0; + virtual void update(const int event = IMUE_SystemRefresh) = 0; protected: const GeneralSettings * generalSettings; @@ -115,21 +123,46 @@ class AbstractItemModel: public QStandardItemModel const Board::Type boardType; private: - ItemModelId m_id = UnknownId; - int m_updateMask = 0; + int m_id = IMID_Unknown; + QString m_name = ""; + int m_updateMask = IMUE_None; + + static QString idToString(const int value); }; class AbstractStaticItemModel: public AbstractItemModel { Q_OBJECT public: - explicit AbstractStaticItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, - Firmware * firmware, const Boards * const board, const Board::Type boardType) : + explicit AbstractStaticItemModel(const GeneralSettings * const generalSettings = nullptr, const ModelData * const modelData = nullptr, + Firmware * firmware = nullptr, const Boards * const board = nullptr, + const Board::Type boardType = Board::BOARD_UNKNOWN) : AbstractItemModel(generalSettings, modelData, firmware, board, boardType) {} virtual ~AbstractStaticItemModel() {}; + inline void appendToItemList(QString text, int id, bool isAvailable = true, int type = 0, int flags = 0) + { itemList.append(new ListItem(text, id, isAvailable, type, flags)); } + + void loadItemList(); + public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override final {} + virtual void update(const int event) override final {} + + protected: + struct ListItem + { + QString text; + int id; + bool isAvailable; + int type; + int flags; + + ListItem() {} + ListItem(QString p_text, int p_id, bool p_isAvailable, int p_type, int p_flags) : + text(p_text), id(p_id), isAvailable(p_isAvailable), type(p_type), flags(p_flags) {} + }; + + QVector itemList; }; class AbstractDynamicItemModel: public AbstractItemModel @@ -142,7 +175,7 @@ class AbstractDynamicItemModel: public AbstractItemModel virtual ~AbstractDynamicItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) {} + virtual void update(const int event = IMUE_SystemRefresh) {} signals: void aboutToBeUpdated(); @@ -158,7 +191,7 @@ class RawSourceItemModel: public AbstractDynamicItemModel virtual ~RawSourceItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const RawSource & src) const; @@ -174,7 +207,7 @@ class RawSwitchItemModel: public AbstractDynamicItemModel virtual ~RawSwitchItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const RawSwitch & rsw) const; @@ -190,7 +223,7 @@ class CurveItemModel: public AbstractDynamicItemModel virtual ~CurveItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const int value) const; @@ -205,7 +238,7 @@ class GVarReferenceItemModel: public AbstractDynamicItemModel virtual ~GVarReferenceItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const AdjustmentReference & ar) const; @@ -221,7 +254,7 @@ class ThrottleSourceItemModel: public AbstractDynamicItemModel virtual ~ThrottleSourceItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const int value) const; @@ -236,7 +269,7 @@ class CustomFuncActionItemModel: public AbstractDynamicItemModel virtual ~CustomFuncActionItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const int value) const; @@ -251,7 +284,7 @@ class CustomFuncResetParamItemModel: public AbstractDynamicItemModel virtual ~CustomFuncResetParamItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const int value) const; @@ -266,7 +299,7 @@ class TelemetrySourceItemModel: public AbstractDynamicItemModel virtual ~TelemetrySourceItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const int value) const; @@ -290,18 +323,31 @@ class CurveRefFuncItemModel : public AbstractStaticItemModel virtual ~CurveRefFuncItemModel() {}; }; -class ItemModelsFactory +class PrecisionItemModel : public AbstractStaticItemModel +{ + Q_OBJECT + public: + explicit PrecisionItemModel(const int minDecimals, const int maxDecimals, const QString suffix = "", const bool placeholders = false); + virtual ~PrecisionItemModel() {}; +}; + +// +// CompoundItemModelFactory +// + +class CompoundItemModelFactory { public: - ItemModelsFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData); - virtual ~ItemModelsFactory(); + CompoundItemModelFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData); + virtual ~CompoundItemModelFactory(); - void addItemModel(const AbstractItemModel::ItemModelId id); - void registerItemModel(AbstractItemModel * itemModel); + void addItemModel(const int id); + int registerItemModel(AbstractItemModel * itemModel); void unregisterItemModels(); - void unregisterItemModel(const AbstractItemModel::ItemModelId id); - AbstractItemModel * getItemModel(const AbstractItemModel::ItemModelId id) const; - void update(const AbstractItemModel::UpdateTrigger trigger = AbstractItemModel::SystemRefresh); + void unregisterItemModel(const int id); + bool isItemModelRegistered(const int id) const; + AbstractItemModel * getItemModel(const int id) const; + void update(const int event = AbstractItemModel::IMUE_SystemRefresh); void dumpAllItemModelContents() const; protected: @@ -311,6 +357,9 @@ class ItemModelsFactory Boards * board; Board::Type boardType; QVector registeredItemModels; + + private: + void setSourceId(AbstractItemModel * itemModel); }; -#endif // RAWITEMDATAMODELS_H +#endif // COMPOUNDITEMMODELS_H diff --git a/companion/src/datamodels/rawitemfilteredmodel.cpp b/companion/src/datamodels/filtereditemmodels.cpp similarity index 51% rename from companion/src/datamodels/rawitemfilteredmodel.cpp rename to companion/src/datamodels/filtereditemmodels.cpp index 1cddff04d..4f4e27262 100644 --- a/companion/src/datamodels/rawitemfilteredmodel.cpp +++ b/companion/src/datamodels/filtereditemmodels.cpp @@ -18,13 +18,15 @@ * GNU General Public License for more details. */ -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" FilteredItemModel::FilteredItemModel(AbstractItemModel * sourceModel, int flags) : QSortFilterProxyModel(nullptr), - filterFlags(0) + filterFlags(0), + m_id(0), + m_name("") { - setFilterRole(AbstractItemModel::IsAvailableRole); + setFilterRole(AbstractItemModel::IMDR_Available); setFilterKeyColumn(0); setFilterFlags(flags); setDynamicSortFilter(true); @@ -55,7 +57,7 @@ bool FilteredItemModel::filterAcceptsRow(int sourceRow, const QModelIndex & sour return true; bool ok; - const int flags = sourceModel()->data(srcIdx, AbstractItemModel::ItemFlagsRole).toInt(&ok); + const int flags = sourceModel()->data(srcIdx, AbstractItemModel::IMDR_Flags).toInt(&ok); return (ok && (!flags || (filterFlags & flags))); } @@ -85,68 +87,85 @@ void FilteredItemModel::dumpItemModelContents(FilteredItemModel * itemModel) for (int i = 0; i < itemModel->rowCount(); ++i) { qDebug() << "row:" << i << "text:" << itemModel->data(itemModel->index(i, 0), Qt::DisplayRole).toString() - << "id:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemIdRole).toInt() - << "avail:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IsAvailableRole).toBool() - << "flags:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemFlagsRole).toInt() - << "type:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemTypeRole).toInt(); + << "id:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Id).toInt() + << "avail:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Available).toBool() + << "flags:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Flags).toInt() + << "type:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Type).toInt(); } } else - qDebug() << "Error: model did not cast"; + qDebug() << "Error: model not of class FilteredItemModel"; } // -// FilteredItemModelsFactory +// FilteredItemModelFactory // -FilteredItemModelsFactory::FilteredItemModelsFactory() +FilteredItemModelFactory::FilteredItemModelFactory() { } -FilteredItemModelsFactory::~FilteredItemModelsFactory() +FilteredItemModelFactory::~FilteredItemModelFactory() { unregisterItemModels(); } -int FilteredItemModelsFactory::registerItemModel(FilteredItemModel * itemModel, QString name) +int FilteredItemModelFactory::registerItemModel(FilteredItemModel * itemModel, const QString name, const int id) { if (itemModel) { - if (isItemModelRegistered(name)) - qDebug() << "Warning: item model already registered with name:" << name; - const int id = registeredItemModels.count(); - itemModel->setId(id); - itemModel->setName(name); - registeredItemModels.push_back(itemModel); - return id; + if (isItemModelRegistered(name)) { + qDebug() << "Error: item model already registered with name:" << name; + return 0; + } + else if (id > -1 && isItemModelRegistered(id)) { + qDebug() << "Error: item model already registered with id:" << id; + return 0; + } + else { + int fid = id; + if (fid < 0) { + fid = registeredItemModels.count() + 1; + AbstractItemModel * mdl = qobject_cast(itemModel->sourceModel()); + if (mdl) { + fid += mdl->getId() * 128; + } + } + itemModel->setId(fid); + itemModel->setName(name); + registeredItemModels.append(itemModel); + return fid; + } } + + qDebug() << "Error: invalid filtered item model pointer"; return 0; } -void FilteredItemModelsFactory::unregisterItemModels() +void FilteredItemModelFactory::unregisterItemModels() { foreach (FilteredItemModel * itemModel, registeredItemModels) { delete itemModel; } } -void FilteredItemModelsFactory::unregisterItemModel(const int id) +void FilteredItemModelFactory::unregisterItemModel(const int id) { FilteredItemModel * itemModel = getItemModel(id); if (itemModel) delete itemModel; } -bool FilteredItemModelsFactory::isItemModelRegistered(const int id) const +bool FilteredItemModelFactory::isItemModelRegistered(const int id) const { return getItemModel(id); } -bool FilteredItemModelsFactory::isItemModelRegistered(const QString name) const +bool FilteredItemModelFactory::isItemModelRegistered(const QString name) const { return getItemModel(name); } -FilteredItemModel * FilteredItemModelsFactory::getItemModel(const int id) const +FilteredItemModel * FilteredItemModelFactory::getItemModel(const int id) const { foreach (FilteredItemModel * itemModel, registeredItemModels) { if (itemModel->getId() == id) @@ -156,7 +175,7 @@ FilteredItemModel * FilteredItemModelsFactory::getItemModel(const int id) const return nullptr; } -FilteredItemModel * FilteredItemModelsFactory::getItemModel(const QString name) const +FilteredItemModel * FilteredItemModelFactory::getItemModel(const QString name) const { foreach (FilteredItemModel * itemModel, registeredItemModels) { if (itemModel->getName() == name) @@ -166,26 +185,68 @@ FilteredItemModel * FilteredItemModelsFactory::getItemModel(const QString name) return nullptr; } -void FilteredItemModelsFactory::update() +void FilteredItemModelFactory::update() { foreach (FilteredItemModel * itemModel, registeredItemModels) { itemModel->update(); } } -void FilteredItemModelsFactory::dumpItemModelContents(const int id) const +void FilteredItemModelFactory::dumpItemModelContents(const int id) const { FilteredItemModel::dumpItemModelContents(getItemModel(id)); } -void FilteredItemModelsFactory::dumpItemModelContents(const QString name) const +void FilteredItemModelFactory::dumpItemModelContents(const QString name) const { FilteredItemModel::dumpItemModelContents(getItemModel(name)); } -void FilteredItemModelsFactory::dumpAllItemModelContents() const +void FilteredItemModelFactory::dumpAllItemModelContents() const { foreach (FilteredItemModel * itemModel, registeredItemModels) { FilteredItemModel::dumpItemModelContents(itemModel); } } + +// +// CurveRefFilteredFactory +// + +CurveRefFilteredFactory::CurveRefFilteredFactory(CompoundItemModelFactory * sharedItemModels, const int curveFlags, const int gvarRefFlags) +{ + if (!sharedItemModels) { + qDebug() << "Error: invalid compound item model factory pointer"; + return; + } + + registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_Curve), curveFlags), + fidToString(CRFIM_CURVE), CRFIM_CURVE); + registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_GVarRef), gvarRefFlags), + fidToString(CRFIM_GVARREF), CRFIM_GVARREF); + registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_CurveRefType)), + fidToString(CRFIM_TYPE), CRFIM_TYPE); + registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_CurveRefFunc)), + fidToString(CRFIM_FUNC), CRFIM_FUNC); +} + +CurveRefFilteredFactory::~CurveRefFilteredFactory() +{ +} + +// static +QString CurveRefFilteredFactory::fidToString(const int value) +{ + switch(value) { + case CRFIM_CURVE: + return "Curves"; + case CRFIM_GVARREF: + return "Global Variables"; + case CRFIM_TYPE: + return "Types"; + case CRFIM_FUNC: + return "Functions"; + default: + return CPN_STR_UNKNOWN_ITEM; + } +} diff --git a/companion/src/datamodels/rawitemfilteredmodel.h b/companion/src/datamodels/filtereditemmodels.h similarity index 61% rename from companion/src/datamodels/rawitemfilteredmodel.h rename to companion/src/datamodels/filtereditemmodels.h index 08bc65993..24668704d 100644 --- a/companion/src/datamodels/rawitemfilteredmodel.h +++ b/companion/src/datamodels/filtereditemmodels.h @@ -18,10 +18,10 @@ * GNU General Public License for more details. */ -#ifndef RAWITEMFILTEREDMODEL_H -#define RAWITEMFILTEREDMODEL_H +#ifndef FILTEREDITEMMODELS_H +#define FILTEREDITEMMODELS_H -#include "rawitemdatamodels.h" +#include "compounditemmodels.h" #include #include @@ -32,12 +32,12 @@ class FilteredItemModel: public QSortFilterProxyModel Q_OBJECT public: enum DataFilters { - AllFilter = AbstractItemModel::NegativeGroup | AbstractItemModel::NoneGroup | AbstractItemModel::PositiveGroup, - AllExcludeNoneFilter = AllFilter &~ AbstractItemModel::NoneGroup, - NegativeFilter = AbstractItemModel::NegativeGroup | AbstractItemModel::NoneGroup, - NegativeExcludeNoneFilter = AbstractItemModel::NegativeGroup, - PositiveFilter = AbstractItemModel::PositiveGroup | AbstractItemModel::NoneGroup, - PositiveExcludeNoneFilter = AbstractItemModel::PositiveGroup + AllFilter = AbstractItemModel::IMDG_Negative | AbstractItemModel::IMDG_None | AbstractItemModel::IMDG_Positive, + AllExcludeNoneFilter = AllFilter &~ AbstractItemModel::IMDG_None, + NegativeFilter = AbstractItemModel::IMDG_Negative | AbstractItemModel::IMDG_None, + NegativeExcludeNoneFilter = AbstractItemModel::IMDG_Negative, + PositiveFilter = AbstractItemModel::IMDG_Positive | AbstractItemModel::IMDG_None, + PositiveExcludeNoneFilter = AbstractItemModel::IMDG_Positive }; Q_ENUM(DataFilters) @@ -73,13 +73,13 @@ class FilteredItemModel: public QSortFilterProxyModel QString m_name = ""; }; -class FilteredItemModelsFactory +class FilteredItemModelFactory { public: - FilteredItemModelsFactory(); - virtual ~FilteredItemModelsFactory(); + explicit FilteredItemModelFactory(); + virtual ~FilteredItemModelFactory(); - int registerItemModel(FilteredItemModel * itemModel, QString name); + int registerItemModel(FilteredItemModel * itemModel, const QString name, const int id = -1); void unregisterItemModels(); void unregisterItemModel(const int id); bool isItemModelRegistered(const int id) const; @@ -96,17 +96,20 @@ class FilteredItemModelsFactory QVector registeredItemModels; }; +class CurveRefFilteredFactory : public FilteredItemModelFactory +{ + public: + enum FilteredItemModelId { + CRFIM_CURVE, + CRFIM_GVARREF, + CRFIM_TYPE, + CRFIM_FUNC + }; -#define FILTEREDITEMMODELCONNECT(fltrmodel) \ - connect(fltrmodel, SIGNAL(aboutToBeUpdated()), this, SLOT(onItemModelAboutToBeUpdated())); \ - connect(fltrmodel, SIGNAL(updateComplete()), this, SLOT(onItemModelUpdateComplete())); + explicit CurveRefFilteredFactory(CompoundItemModelFactory * sharedItemModels, const int curveFlags = 0, const int gvarRefFlags = 0); + virtual ~CurveRefFilteredFactory(); -#define FILTEREDITEMMODEL(fltrmodel, class, modelid, flags) \ - fltrmodel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::modelid), flags); \ - FILTEREDITEMMODELCONNECT(fltrmodel) + static QString fidToString(const int value); +}; -#define FILTEREDITEMMODELNOFLAGS(fltrmodel, class, modelid) \ - fltrmodel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::modelid)); \ - FILTEREDITEMMODELCONNECT(fltrmodel) - -#endif // RAWITEMFILTEREDMODEL_H +#endif // FILTEREDITEMMODELS_H diff --git a/companion/src/datamodels/itemmodelfactory.cpp b/companion/src/datamodels/itemmodelfactory.cpp index 892345568..b5b341afd 100644 --- a/companion/src/datamodels/itemmodelfactory.cpp +++ b/companion/src/datamodels/itemmodelfactory.cpp @@ -20,8 +20,8 @@ #include "itemmodelfactory.h" -#include "rawitemdatamodels.h" -#include "rawitemfilteredmodel.h" +#include "compounditemmodels.h" +#include "filtereditemmodels.h" #include "eeprominterface.h" #include "generalsettings.h" @@ -102,7 +102,7 @@ void ItemModelFactory::unregisterItemModel(const AbstractItemModel::ItemModelId delete itemModel; } -QAbstractItemModel * ItemModelsFactory::getItemModel(const AbstractItemModel::ItemModelId id) const +QAbstractItemModel * CompoundItemModelFactory::getItemModel(const AbstractItemModel::ItemModelId id) const { foreach (AbstractItemModel * itemModel, registeredItemModels) { if (itemModel->getId() == id) @@ -127,7 +127,7 @@ void ItemModelFactory::dumpAllItemModelContents() const } // -// FilteredItemModelsFactory +// FilteredItemModelFactory // FilteredItemModelFactory::FilteredItemModelFactory() diff --git a/companion/src/datamodels/itemmodelfactory.h b/companion/src/datamodels/itemmodelfactory.h index 01b7626b4..9f03b897b 100644 --- a/companion/src/datamodels/itemmodelfactory.h +++ b/companion/src/datamodels/itemmodelfactory.h @@ -49,11 +49,11 @@ class ItemModelFactory QVector registeredItemModels; }; -class FilteredItemModelsFactory +class FilteredItemModelFactory { public: - FilteredItemModelsFactory(); - virtual ~FilteredItemModelsFactory(); + FilteredItemModelFactory(); + virtual ~FilteredItemModelFactory(); int registerItemModel(FilteredItemModel * itemModel, QString name); void unregisterItemModels(); diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 4d961630a..6ade9264e 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -22,7 +22,7 @@ #include "adjustmentreference.h" #include "helpers.h" #include "modeldata.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" const QString CurveReference::toString(const ModelData * model, bool verbose) const { @@ -144,29 +144,9 @@ int CurveReference::functionCount() * CurveReferenceUIManager */ -CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveValueCB, CurveReference & curveRef, const ModelData & model, - FilteredItemModel * curveItemModel, FilteredItemModel * gvarItemModel, - FilteredItemModel * typeItemModel, FilteredItemModel * funcItemModel, QObject * parent) : - QObject(parent), - curveTypeCB(nullptr), - curveGVarCB(nullptr), - curveValueSB(nullptr), - curveValueCB(curveValueCB), - curveRef(curveRef), - model(model), - lock(false), - curveItemModel(curveItemModel), - gvarItemModel(gvarItemModel), - typeItemModel(typeItemModel), - funcItemModel(funcItemModel) -{ - init(parent); -} - CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheckBox * curveGVarCB, QSpinBox * curveValueSB, QComboBox * curveValueCB, CurveReference & curveRef, const ModelData & model, - FilteredItemModel * curveItemModel, FilteredItemModel * gvarItemModel, - FilteredItemModel * typeItemModel, FilteredItemModel * funcItemModel, QObject * parent) : + CurveRefFilteredFactory * curveRefFilteredFactory, QObject * parent) : QObject(parent), curveTypeCB(curveTypeCB), curveGVarCB(curveGVarCB), @@ -175,24 +155,12 @@ CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheck curveRef(curveRef), model(model), lock(false), - curveItemModel(curveItemModel), - gvarItemModel(gvarItemModel), - typeItemModel(typeItemModel), - funcItemModel(funcItemModel) -{ - init(parent); -} - -CurveReferenceUIManager::~CurveReferenceUIManager() -{ -} - -void CurveReferenceUIManager::init(QObject * parent) + filteredModelFactory(curveRefFilteredFactory) { hasCapabilityGvars = getCurrentFirmware()->getCapability(Gvars); if (curveTypeCB) { - curveTypeCB->setModel(typeItemModel); + curveTypeCB->setModel(filteredModelFactory->getItemModel(CurveRefFilteredFactory::CRFIM_TYPE)); curveTypeCB->setCurrentIndex(curveTypeCB->findData((int)curveRef.type)); connect(curveTypeCB, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int))); } @@ -215,6 +183,11 @@ void CurveReferenceUIManager::init(QObject * parent) update(); } +CurveReferenceUIManager::~CurveReferenceUIManager() +{ + delete filteredModelFactory; +} + #define CURVE_REF_UI_GVAR_SHOW (1<<0) #define CURVE_REF_UI_VALUE_SHOW (1<<1) #define CURVE_REF_UI_REF_SHOW (1<<2) @@ -297,13 +270,13 @@ void CurveReferenceUIManager::populateValueCB(QComboBox * cb) switch (curveRef.type) { case CurveReference::CURVE_REF_DIFF: case CurveReference::CURVE_REF_EXPO: - cb->setModel(gvarItemModel); + cb->setModel(filteredModelFactory->getItemModel(CurveRefFilteredFactory::CRFIM_GVARREF)); break; case CurveReference::CURVE_REF_FUNC: - cb->setModel(funcItemModel); + cb->setModel(filteredModelFactory->getItemModel(CurveRefFilteredFactory::CRFIM_FUNC)); break; case CurveReference::CURVE_REF_CUSTOM: - cb->setModel(curveItemModel); + cb->setModel(filteredModelFactory->getItemModel(CurveRefFilteredFactory::CRFIM_CURVE)); break; default: break; diff --git a/companion/src/firmwares/curvereference.h b/companion/src/firmwares/curvereference.h index 3d61e3cb5..c4482c742 100644 --- a/companion/src/firmwares/curvereference.h +++ b/companion/src/firmwares/curvereference.h @@ -29,7 +29,7 @@ class ModelData; -class FilteredItemModel; +class CurveRefFilteredFactory; class CurveReference { @@ -89,14 +89,13 @@ class CurveReferenceUIManager : public QObject { Q_OBJECT public: - CurveReferenceUIManager(QComboBox *curveValueCB, CurveReference & curve, const ModelData & model, - FilteredItemModel * curveItemModel, FilteredItemModel * gvarItemModel, FilteredItemModel * typeItemModel, - FilteredItemModel * funcItemModel, QObject * parent = nullptr); + explicit CurveReferenceUIManager(QComboBox *curveTypeCB, QCheckBox *curveGVarCB, QSpinBox *curveValueSB, QComboBox *curveValueCB, + CurveReference & curve, const ModelData & model, CurveRefFilteredFactory * curveRefFilteredFactory, + QObject * parent = nullptr); - CurveReferenceUIManager(QComboBox *curveTypeCB, QCheckBox *curveGVarCB, QSpinBox *curveValueSB, QComboBox *curveValueCB, - CurveReference & curve, const ModelData & model, FilteredItemModel * curveItemModel, - FilteredItemModel * gvarItemModel, FilteredItemModel * typeItemModel, - FilteredItemModel * funcItemModel, QObject * parent = nullptr); + explicit CurveReferenceUIManager(QComboBox *curveValueCB, CurveReference & curve, const ModelData & model, + CurveRefFilteredFactory * curveRefFilteredFactory, QObject * parent = nullptr) : + CurveReferenceUIManager(nullptr, nullptr, nullptr, curveValueCB, curve, model, curveRefFilteredFactory, parent) {} virtual ~CurveReferenceUIManager(); @@ -116,12 +115,7 @@ class CurveReferenceUIManager : public QObject { const ModelData & model; bool lock; bool hasCapabilityGvars; - FilteredItemModel * curveItemModel; - FilteredItemModel * gvarItemModel; - FilteredItemModel * typeItemModel; - FilteredItemModel * funcItemModel; - - void init(QObject * parent); + CurveRefFilteredFactory *filteredModelFactory; void populateValueCB(QComboBox * cb); }; diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index 2b01f2600..753d2eed0 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -23,6 +23,7 @@ #include "radiodata.h" #include "modeldata.h" #include "eeprominterface.h" +#include "compounditemmodels.h" void SensorData::updateUnit() { @@ -32,9 +33,18 @@ void SensorData::updateUnit() } } +// TODO depreciated QString SensorData::unitString() const { - switch (unit) { + return unitToString(unit); +} + +// static +QString SensorData::unitToString(const int value) +{ + switch (value) { + case UNIT_RAW: + return tr("Raw (-)"); case UNIT_VOLTS: return tr("V"); case UNIT_AMPS: @@ -92,7 +102,7 @@ QString SensorData::unitString() const case UNIT_US: return tr("uS"); default: - return ""; + return CPN_STR_UNKNOWN_ITEM; } } @@ -120,11 +130,71 @@ bool SensorData::isEmpty() const return (!isAvailable() && type == 0 && id == 0 && subid == 0 && instance == 0 && rxIdx == 0 && moduleIdx == 0 && unit == 0 && ratio == 0 && prec == 0 && offset == 0 && autoOffset == 0 && filter == 0 && onlyPositive == 0 && logs == 0); } +// static +QString SensorData::typeToString(const int value) +{ + switch(value) { + case TELEM_TYPE_CUSTOM: + return tr("Custom"); + case TELEM_TYPE_CALCULATED: + return tr("Calculated"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString SensorData::formulaToString(const int value) +{ + switch(value) { + case TELEM_FORMULA_ADD: + return tr("Add"); + case TELEM_FORMULA_AVERAGE: + return tr("Average"); + case TELEM_FORMULA_MIN: + return tr("Minimum"); + case TELEM_FORMULA_MAX: + return tr("Maximum"); + case TELEM_FORMULA_MULTIPLY: + return tr("Multiply"); + case TELEM_FORMULA_TOTALIZE: + return tr("Totalize"); + case TELEM_FORMULA_CELL: + return tr("Cell"); + case TELEM_FORMULA_CONSUMPTION: + return tr("Consumption"); + case TELEM_FORMULA_DIST: + return tr("Distance"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString SensorData::cellIndexToString(const int value) +{ + if (value == TELEM_CELL_INDEX_LOWEST) + return tr("Lowest"); + if (value > TELEM_CELL_INDEX_LOWEST && value < TELEM_CELL_INDEX_HIGHEST) + return tr("Cell %1").arg(value - TELEM_CELL_INDEX_LOWEST); + if (value== TELEM_CELL_INDEX_HIGHEST) + return tr("Highest"); + if (value == TELEM_CELL_INDEX_DELTA) + return tr("Delta"); + return CPN_STR_UNKNOWN_ITEM; +} + +// static +QString SensorData::precisionToString(const int value) +{ + return QString("0.%1").arg(QString(value, '0')); +} + // static QString SensorData::sourceToString(const ModelData * model, const int index, const bool sign) { if (model) { - const QString prfx = sign ? index < 0 ? "-" : "" : ""; + const QString prfx = sign ? index < 0 ? "-" : "+" : ""; if (abs(index) > 0) { const SensorData &sd = model->sensorData[abs(index) - 1]; @@ -136,23 +206,89 @@ QString SensorData::sourceToString(const ModelData * model, const int index, con else return CPN_STR_NONE_ITEM; } - else - return ""; + + return ""; } // static bool SensorData::isSourceAvailable(const ModelData * model, const int index) { - if (model) { - Firmware * firmware = getCurrentFirmware(); - const int i = abs(index); - if (i <= firmware->getCapability(Sensors)) { - if (i > 0) - return model->sensorData[i - 1].isAvailable(); - else - return true; - } + Firmware * firmware = getCurrentFirmware(); + const int sensorcnt = firmware->getCapability(Sensors); + const int i = abs(index); + + if (model && sensorcnt > 0 and i <= sensorcnt) { + if (i > 0) + return model->sensorData[i - 1].isAvailable(); + else + return true; } return false; } + +// static +AbstractStaticItemModel * SensorData::typeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("sensordata.type"); + + for (int i = 0; i <= TELEM_TYPE_LAST; i++) { + mdl->appendToItemList(typeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * SensorData::formulaItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("sensordata.formula"); + + for (int i = 0; i <= TELEM_FORMULA_LAST; i++) { + mdl->appendToItemList(formulaToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * SensorData::cellIndexItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("sensordata.cellindex"); + + for (int i = 0; i <= TELEM_CELL_INDEX_LAST; i++) { + mdl->appendToItemList(cellIndexToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * SensorData::unitItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("sensordata.unit"); + + for (int i = 0; i <= UNIT_MAX; i++) { + QString str = unitToString(i); + if (str != CPN_STR_UNKNOWN_ITEM) + mdl->appendToItemList(str, i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +PrecisionItemModel * SensorData::precisionItemModel() +{ + PrecisionItemModel * mdl = new PrecisionItemModel(0, 2); + mdl->setName("sensordata.precision"); + return mdl; +} diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index 200f6e889..afd9d7891 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -27,6 +27,8 @@ constexpr int CPN_MAX_SENSORS = 60; constexpr int SENSOR_LABEL_LEN = 4; class ModelData; +class AbstractStaticItemModel; +class PrecisionItemModel; class SensorData { Q_DECLARE_TR_FUNCTIONS(SensorData) @@ -36,7 +38,8 @@ class SensorData { enum { TELEM_TYPE_CUSTOM, - TELEM_TYPE_CALCULATED + TELEM_TYPE_CALCULATED, + TELEM_TYPE_LAST = TELEM_TYPE_CALCULATED }; enum @@ -63,6 +66,7 @@ class SensorData { TELEM_CELL_INDEX_6, TELEM_CELL_INDEX_HIGHEST, TELEM_CELL_INDEX_DELTA, + TELEM_CELL_INDEX_LAST = TELEM_CELL_INDEX_DELTA }; enum @@ -169,8 +173,19 @@ class SensorData { void clear() { memset(this, 0, sizeof(SensorData)); } bool isEmpty() const; - static QString sourceToString(const ModelData * model, const int index, const bool sign = true); + static QString sourceToString(const ModelData * model, const int index, const bool sign = false); static bool isSourceAvailable(const ModelData * model, const int index); + static QString typeToString(const int value); + static QString formulaToString(const int value); + static QString cellIndexToString(const int value); + static QString unitToString(const int value); + static QString precisionToString(const int value); + + static AbstractStaticItemModel * typeItemModel(); + static AbstractStaticItemModel * formulaItemModel(); + static AbstractStaticItemModel * cellIndexItemModel(); + static AbstractStaticItemModel * unitItemModel(); + static PrecisionItemModel * precisionItemModel(); }; #endif // SENSORDATA_H diff --git a/companion/src/generaledit/generaledit.cpp b/companion/src/generaledit/generaledit.cpp index c2a38b61d..e80038f9f 100644 --- a/companion/src/generaledit/generaledit.cpp +++ b/companion/src/generaledit/generaledit.cpp @@ -28,7 +28,7 @@ #include "hardware.h" #include "../modeledit/customfunctions.h" #include "verticalscrollarea.h" -#include "rawitemdatamodels.h" +#include "compounditemmodels.h" GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * firmware) : QDialog(parent), @@ -56,11 +56,11 @@ GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * fir } } - sharedItemModels = new ItemModelsFactory(&generalSettings, nullptr); - sharedItemModels->addItemModel(AbstractItemModel::RawSourceId); - sharedItemModels->addItemModel(AbstractItemModel::RawSwitchId); - sharedItemModels->addItemModel(AbstractItemModel::CustomFuncActionId); - sharedItemModels->addItemModel(AbstractItemModel::CustomFuncResetParamId); + sharedItemModels = new CompoundItemModelFactory(&generalSettings, nullptr); + sharedItemModels->addItemModel(AbstractItemModel::IMID_RawSource); + sharedItemModels->addItemModel(AbstractItemModel::IMID_RawSwitch); + sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncAction); + sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncResetParam); addTab(new GeneralSetupPanel(this, generalSettings, firmware), tr("Setup")); addTab(new CustomFunctionsPanel(this, nullptr, generalSettings, firmware, sharedItemModels), tr("Global Functions")); diff --git a/companion/src/generaledit/generaledit.h b/companion/src/generaledit/generaledit.h index b4a2ea3cc..b2e6caa82 100644 --- a/companion/src/generaledit/generaledit.h +++ b/companion/src/generaledit/generaledit.h @@ -25,7 +25,7 @@ #include "eeprominterface.h" #include "genericpanel.h" -class ItemModelsFactory; +class CompoundItemModelFactory; namespace Ui { class GeneralEdit; @@ -73,7 +73,7 @@ class GeneralEdit : public QDialog QVector panels; void addTab(GenericPanel *panel, QString text); void closeEvent(QCloseEvent *event); - ItemModelsFactory *sharedItemModels; + CompoundItemModelFactory *sharedItemModels; }; #endif // _GENERALEDIT_H_ diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index cd83675dc..27374a8c4 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -20,7 +20,7 @@ #include "channels.h" #include "helpers.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" LimitsGroup::LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row, int col, int & value, const ModelData & model, int min, int max, int deflt, ModelPanel * panel): firmware(firmware), @@ -96,14 +96,15 @@ void LimitsGroup::updateMinMax(int max) } } } -ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): +ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), sharedItemModels(sharedItemModels) { chnCapability = firmware->getCapability(Outputs); int channelNameMaxLen = firmware->getCapability(ChannelsName); - FILTEREDITEMMODELNOFLAGS(curveFilteredModel, ChannelsPanel, CurveId) + curveFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_Curve)); + connectItemModelEvents(curveFilteredModel); QStringList headerLabels; headerLabels << "#"; @@ -473,6 +474,12 @@ void ChannelsPanel::swapData(int idx1, int idx2) } } +void ChannelsPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &ChannelsPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &ChannelsPanel::onItemModelUpdateComplete); +} + void ChannelsPanel::onItemModelAboutToBeUpdated() { lock = true; @@ -486,5 +493,5 @@ void ChannelsPanel::onItemModelUpdateComplete() void ChannelsPanel::updateItemModels() { - sharedItemModels->update(AbstractItemModel::ChannelsUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_Channels); } diff --git a/companion/src/modeledit/channels.h b/companion/src/modeledit/channels.h index 7e2d3f3fa..2a65f497a 100644 --- a/companion/src/modeledit/channels.h +++ b/companion/src/modeledit/channels.h @@ -26,7 +26,7 @@ #include -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; constexpr char MIMETYPE_CHANNEL[] = "application/x-companion-channel"; @@ -57,7 +57,7 @@ class ChannelsPanel : public ModelPanel Q_OBJECT public: - ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~ChannelsPanel(); public slots: @@ -100,9 +100,10 @@ class ChannelsPanel : public ModelPanel QCheckBox *symlimitsChk[CPN_MAX_CHNOUT]; int selectedIndex; int chnCapability; - ItemModelsFactory *sharedItemModels; + CompoundItemModelFactory *sharedItemModels; FilteredItemModel *curveFilteredModel; void updateItemModels(); + void connectItemModelEvents(const FilteredItemModel * itemModel); }; #endif // _CHANNELS_H_ diff --git a/companion/src/modeledit/curves.cpp b/companion/src/modeledit/curves.cpp index 34cda04f9..7f8b80219 100644 --- a/companion/src/modeledit/curves.cpp +++ b/companion/src/modeledit/curves.cpp @@ -23,7 +23,7 @@ #include "node.h" #include "edge.h" #include "helpers.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #define GFX_MARGIN 16 @@ -110,7 +110,7 @@ float curveSymmetricalX(float x, float coeff, float yMin, float yMid, float yMax } CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels): + CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Curves), currentCurve(0), @@ -813,7 +813,7 @@ void CurvesPanel::swapData(int idx1, int idx2) void CurvesPanel::updateItemModels() { - sharedItemModels->update(AbstractItemModel::CurvesUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_Curves); } CustomScene::CustomScene(QGraphicsView * view) : diff --git a/companion/src/modeledit/curves.h b/companion/src/modeledit/curves.h index 46ac63a01..c66f79cb7 100644 --- a/companion/src/modeledit/curves.h +++ b/companion/src/modeledit/curves.h @@ -26,7 +26,7 @@ #include #include -class ItemModelsFactory; +class CompoundItemModelFactory; constexpr char MIMETYPE_CURVE[] = "application/x-companion-curve"; @@ -61,7 +61,7 @@ class CurvesPanel : public ModelPanel Q_OBJECT public: - CurvesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + CurvesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~CurvesPanel(); virtual void update(); @@ -121,7 +121,7 @@ class CurvesPanel : public ModelPanel bool moveDownAllowed() const; bool moveUpAllowed() const; void swapData(int idx1, int idx2); - ItemModelsFactory * sharedItemModels; + CompoundItemModelFactory * sharedItemModels; void updateItemModels(); }; diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 94949064b..5c15bd538 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -19,7 +19,7 @@ */ #include "customfunctions.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #include "helpers.h" #include "appdata.h" @@ -67,7 +67,7 @@ void RepeatComboBox::update() } CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels): + CompoundItemModelFactory * sharedItemModels): GenericPanel(parent, model, generalSettings, firmware), functions(model ? model->customFn : generalSettings.customFn), mediaPlayerCurrent(-1), @@ -77,10 +77,18 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, lock = true; fswCapability = model ? firmware->getCapability(CustomFunctions) : firmware->getCapability(GlobalFunctions); - FILTEREDITEMMODEL(rawSwitchFilteredModel, CustomFunctionsPanel, RawSwitchId, model ? RawSwitch::SpecialFunctionsContext : RawSwitch::GlobalFunctionsContext) - FILTEREDITEMMODELNOFLAGS(rawSourceAllModel, CustomFunctionsPanel, RawSourceId) - FILTEREDITEMMODEL(rawSourceInputsModel, CustomFunctionsPanel, RawSourceId, RawSource::InputSourceGroups) - FILTEREDITEMMODEL(rawSourceGVarsModel, CustomFunctionsPanel, RawSourceId, RawSource::GVarsGroup) + rawSwitchFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), + model ? RawSwitch::SpecialFunctionsContext : RawSwitch::GlobalFunctionsContext); + connectItemModelEvents(rawSwitchFilteredModel); + + rawSourceAllModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource)); + connectItemModelEvents(rawSourceAllModel); + + rawSourceInputsModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), RawSource::InputSourceGroups); + connectItemModelEvents(rawSourceInputsModel); + + rawSourceGVarsModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), RawSource::GVarsGroup); + connectItemModelEvents(rawSourceGVarsModel); if (!firmware->getCapability(VoicesAsNumbers)) { tracksSet = getFilesSet(getSoundsPath(generalSettings), QStringList() << "*.wav" << "*.WAV", firmware->getCapability(VoicesMaxLength)); @@ -820,6 +828,12 @@ bool CustomFunctionsPanel::moveUpAllowed() const return selectedIndex > 0; } +void CustomFunctionsPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &CustomFunctionsPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &CustomFunctionsPanel::onItemModelUpdateComplete); +} + void CustomFunctionsPanel::onItemModelAboutToBeUpdated() { lock = true; diff --git a/companion/src/modeledit/customfunctions.h b/companion/src/modeledit/customfunctions.h index ef8e68b5f..e41baf672 100644 --- a/companion/src/modeledit/customfunctions.h +++ b/companion/src/modeledit/customfunctions.h @@ -26,7 +26,7 @@ #include -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; class TimerEdit; @@ -55,7 +55,7 @@ class CustomFunctionsPanel : public GenericPanel Q_OBJECT public: - CustomFunctionsPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + CustomFunctionsPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~CustomFunctionsPanel(); virtual void update(); @@ -96,6 +96,8 @@ class CustomFunctionsPanel : public GenericPanel bool moveUpAllowed() const; void swapData(int idx1, int idx2); void resetCBsAndRefresh(int idx); + void connectItemModelEvents(const FilteredItemModel * itemModel); + FilteredItemModel * rawSwitchFilteredModel; FilteredItemModel * rawSourceAllModel; FilteredItemModel * rawSourceInputsModel; diff --git a/companion/src/modeledit/expodialog.cpp b/companion/src/modeledit/expodialog.cpp index 1bf22c68e..808cff7ba 100644 --- a/companion/src/modeledit/expodialog.cpp +++ b/companion/src/modeledit/expodialog.cpp @@ -20,13 +20,11 @@ #include "expodialog.h" #include "ui_expodialog.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #include "helpers.h" ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, GeneralSettings & generalSettings, - Firmware * firmware, QString & inputName, FilteredItemModel * rawSourceFilteredModel, - FilteredItemModel * rawSwitchFilteredModel, FilteredItemModel * curveFilteredModel, - FilteredItemModel * gvarFilteredModel, FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel) : + Firmware * firmware, QString & inputName, CompoundItemModelFactory * sharedItemModels) : QDialog(parent), ui(new Ui::ExpoDialog), model(model), @@ -39,6 +37,9 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G { ui->setupUi(this); + dialogFilteredItemModels = new FilteredItemModelFactory(); + int id; + QLabel * lb_fp[CPN_MAX_FLIGHT_MODES] = {ui->lb_FP0, ui->lb_FP1, ui->lb_FP2, ui->lb_FP3, ui->lb_FP4, ui->lb_FP5, ui->lb_FP6, ui->lb_FP7, ui->lb_FP8 }; QCheckBox * tmp[CPN_MAX_FLIGHT_MODES] = {ui->cb_FP0, ui->cb_FP1, ui->cb_FP2, ui->cb_FP3, ui->cb_FP4, ui->cb_FP5, ui->cb_FP6, ui->cb_FP7, ui->cb_FP8 }; for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) { @@ -51,10 +52,15 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, -100, 100); gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, ed->offset, model, 0, -100, 100); - curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, - curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel, this); - ui->switchesCB->setModel(rawSwitchFilteredModel); + curveRefFilteredItemModels = new CurveRefFilteredFactory(sharedItemModels, + firmware->getCapability(HasInputDiff) ? 0 : FilteredItemModel::PositiveFilter); + curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, + curveRefFilteredItemModels, this); + + id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), + RawSwitch::MixesContext), "RawSwitch"); + ui->switchesCB->setModel(dialogFilteredItemModels->getItemModel(id)); ui->switchesCB->setCurrentIndex(ui->switchesCB->findData(ed->swtch.toValue())); ui->sideCB->setCurrentIndex(ed->mode - 1); @@ -85,7 +91,10 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G if (firmware->getCapability(VirtualInputs)) { ui->inputName->setMaxLength(firmware->getCapability(InputsLength)); - ui->sourceCB->setModel(rawSourceFilteredModel); + id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), + (RawSource::InputSourceGroups & ~ RawSource::NoneGroup & ~RawSource::InputsGroup)), + "RawSource"); + ui->sourceCB->setModel(dialogFilteredItemModels->getItemModel(id)); ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(ed->srcRaw.toValue())); ui->inputName->setValidator(new QRegExpValidator(rx, this)); ui->inputName->setText(inputName); @@ -137,9 +146,10 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G ExpoDialog::~ExpoDialog() { + delete ui; delete gvWeightGroup; delete gvOffsetGroup; - delete ui; + delete dialogFilteredItemModels; } void ExpoDialog::updateScale() diff --git a/companion/src/modeledit/expodialog.h b/companion/src/modeledit/expodialog.h index 427fe9572..f4980d344 100644 --- a/companion/src/modeledit/expodialog.h +++ b/companion/src/modeledit/expodialog.h @@ -26,7 +26,9 @@ #include "modelprinter.h" class GVarGroup; -class FilteredItemModel; +class CompoundItemModelFactory; +class FilteredItemModelFactory; +class CurveRefFilteredFactory; class CurveReferenceUIManager; namespace Ui { @@ -37,9 +39,7 @@ class ExpoDialog : public QDialog { Q_OBJECT public: ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expodata, GeneralSettings & generalSettings, - Firmware * firmware, QString & inputName, FilteredItemModel * rawSourceFilteredModel, - FilteredItemModel * rawSwitchFilteredModel, FilteredItemModel * curveFilteredModel, FilteredItemModel * gvarFilteredModel, - FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel); + Firmware * firmware, QString & inputName, CompoundItemModelFactory * sharedItemModels); virtual ~ExpoDialog(); protected: @@ -66,6 +66,8 @@ class ExpoDialog : public QDialog { ModelPrinter modelPrinter; bool lock; QCheckBox * cb_fp[CPN_MAX_FLIGHT_MODES]; + FilteredItemModelFactory *dialogFilteredItemModels; + CurveRefFilteredFactory *curveRefFilteredItemModels; }; #endif // _EXPODIALOG_H_ diff --git a/companion/src/modeledit/flightmodes.cpp b/companion/src/modeledit/flightmodes.cpp index a60c5f09b..224d69965 100644 --- a/companion/src/modeledit/flightmodes.cpp +++ b/companion/src/modeledit/flightmodes.cpp @@ -20,7 +20,7 @@ #include "flightmodes.h" #include "ui_flightmode.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #include "helpers.h" #include "customdebug.h" @@ -32,8 +32,7 @@ FlightModePanel::FlightModePanel(QWidget * parent, ModelData & model, int phaseI phase(model.flightModeData[phaseIdx]) { ui->setupUi(this); - connect(rawSwitchFilteredModel, &FilteredItemModel::aboutToBeUpdated, this, &FlightModePanel::onItemModelAboutToBeUpdated); - connect(rawSwitchFilteredModel, &FilteredItemModel::updateComplete, this, &FlightModePanel::onItemModelUpdateComplete); + connectItemModelEvents(rawSwitchFilteredModel); ui->labelName->setContextMenuPolicy(Qt::CustomContextMenu); ui->labelName->setToolTip(tr("Popup menu available")); @@ -1362,6 +1361,12 @@ void FlightModePanel::gvSwapData(int idx1, int idx2) } } +void FlightModePanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &FlightModePanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &FlightModePanel::onItemModelUpdateComplete); +} + void FlightModePanel::onItemModelAboutToBeUpdated() { lock = true; @@ -1376,11 +1381,12 @@ void FlightModePanel::onItemModelUpdateComplete() /**********************************************************/ FlightModesPanel::FlightModesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels): + CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), sharedItemModels(sharedItemModels) { - FILTEREDITEMMODEL(rawSwitchFilteredModel, FlightModesPanel, RawSwitchId, RawSwitch::MixesContext) + rawSwitchFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), RawSwitch::MixesContext); + connectItemModelEvents(rawSwitchFilteredModel); modesCount = firmware->getCapability(FlightModes); @@ -1435,6 +1441,12 @@ void FlightModesPanel::update() emit updated(); } +void FlightModesPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &FlightModesPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &FlightModesPanel::onItemModelUpdateComplete); +} + void FlightModesPanel::onItemModelAboutToBeUpdated() { } @@ -1445,8 +1457,8 @@ void FlightModesPanel::onItemModelUpdateComplete() void FlightModesPanel::updateItemModels() { - sharedItemModels->update(AbstractItemModel::FlightModesUpdated); - sharedItemModels->update(AbstractItemModel::GVarsUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_FlightModes); + sharedItemModels->update(AbstractItemModel::IMUE_GVars); } void FlightModesPanel::onTabIndexChanged(int index) diff --git a/companion/src/modeledit/flightmodes.h b/companion/src/modeledit/flightmodes.h index e74dec8bb..33fd87e8a 100644 --- a/companion/src/modeledit/flightmodes.h +++ b/companion/src/modeledit/flightmodes.h @@ -24,7 +24,7 @@ #include "modeledit.h" #include "eeprominterface.h" -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; constexpr char MIMETYPE_FLIGHTMODE[] = "application/x-companion-flightmode"; @@ -139,6 +139,7 @@ class FlightModePanel : public ModelPanel bool gvMoveDownAllowed() const; bool gvMoveUpAllowed() const; void gvSwapData(int idx1, int idx2); + void connectItemModelEvents(const FilteredItemModel * itemModel); }; class FlightModesPanel : public ModelPanel @@ -146,7 +147,7 @@ class FlightModesPanel : public ModelPanel Q_OBJECT public: - FlightModesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + FlightModesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~FlightModesPanel(); public slots: @@ -166,10 +167,11 @@ class FlightModesPanel : public ModelPanel int modesCount; QTabWidget *tabWidget; - ItemModelsFactory *sharedItemModels; + CompoundItemModelFactory *sharedItemModels; FilteredItemModel *rawSwitchFilteredModel; QVector panels; void updateItemModels(); + void connectItemModelEvents(const FilteredItemModel * itemModel); }; #endif // _FLIGHTMODES_H_ diff --git a/companion/src/modeledit/heli.cpp b/companion/src/modeledit/heli.cpp index a1142468e..ef86c5793 100644 --- a/companion/src/modeledit/heli.cpp +++ b/companion/src/modeledit/heli.cpp @@ -21,15 +21,16 @@ #include "heli.h" #include "ui_heli.h" #include "helpers.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" -HeliPanel::HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): +HeliPanel::HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Heli) { ui->setupUi(this); - FILTEREDITEMMODEL(rawSourceFilteredModel, HeliPanel, RawSourceId, RawSource::InputSourceGroups) + rawSourceFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), RawSource::InputSourceGroups); + connectItemModelEvents(rawSourceFilteredModel); connect(ui->swashType, SIGNAL(currentIndexChanged(int)), this, SLOT(edited())); connect(ui->swashRingVal, SIGNAL(editingFinished()), this, SLOT(edited())); @@ -116,6 +117,12 @@ void HeliPanel::edited() } } +void HeliPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &HeliPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &HeliPanel::onItemModelUpdateComplete); +} + void HeliPanel::onItemModelAboutToBeUpdated() { lock = true; diff --git a/companion/src/modeledit/heli.h b/companion/src/modeledit/heli.h index 2238a67f1..eb3c7d54e 100644 --- a/companion/src/modeledit/heli.h +++ b/companion/src/modeledit/heli.h @@ -23,7 +23,7 @@ #include "modeledit.h" -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; namespace Ui { @@ -35,7 +35,7 @@ class HeliPanel : public ModelPanel Q_OBJECT public: - HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~HeliPanel(); void update(); @@ -47,6 +47,7 @@ class HeliPanel : public ModelPanel private: Ui::Heli *ui; FilteredItemModel * rawSourceFilteredModel; + void connectItemModelEvents(const FilteredItemModel * itemModel); }; #endif // _HELI_H_ diff --git a/companion/src/modeledit/inputs.cpp b/companion/src/modeledit/inputs.cpp index ae25ae451..f48d7d916 100644 --- a/companion/src/modeledit/inputs.cpp +++ b/companion/src/modeledit/inputs.cpp @@ -21,20 +21,19 @@ #include "inputs.h" #include "expodialog.h" #include "helpers.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" -InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): +InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), expoInserted(false), modelPrinter(firmware, generalSettings, model), - sharedItemModels(sharedItemModels) + sharedItemModels(sharedItemModels), + modelsUpdateCnt(0) { - FILTEREDITEMMODEL(rawSourceFilteredModel, InputsPanel, RawSourceId,(RawSource::InputSourceGroups & ~ RawSource::NoneGroup & ~RawSource::InputsGroup)) - FILTEREDITEMMODEL(rawSwitchFilteredModel, InputsPanel, RawSwitchId, RawSwitch::MixesContext) - FILTEREDITEMMODEL(curveFilteredModel, InputsPanel, CurveId, firmware->getCapability(HasInputDiff) ? 0 : FilteredItemModel::PositiveFilter) - FILTEREDITEMMODELNOFLAGS(gvarFilteredModel, InputsPanel, GVarRefId) - FILTEREDITEMMODELNOFLAGS(crTypeFilteredModel, InputsPanel, CurveRefTypeId) - FILTEREDITEMMODELNOFLAGS(crFuncFilteredModel, InputsPanel, CurveRefFuncId) + connectItemModelEvents(AbstractItemModel::IMID_RawSource); + connectItemModelEvents(AbstractItemModel::IMID_RawSwitch); + connectItemModelEvents(AbstractItemModel::IMID_Curve); + connectItemModelEvents(AbstractItemModel::IMID_GVarRef); inputsCount = firmware->getCapability(VirtualInputs); if (inputsCount == 0) @@ -74,12 +73,6 @@ InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & g InputsPanel::~InputsPanel() { - delete rawSourceFilteredModel; - delete rawSwitchFilteredModel; - delete curveFilteredModel; - delete gvarFilteredModel; - delete crTypeFilteredModel; - delete crFuncFilteredModel; } void InputsPanel::update() @@ -195,8 +188,7 @@ void InputsPanel::gm_openExpo(int index) if (firmware->getCapability(VirtualInputs)) inputName = model->inputNames[ed.chn]; - ExpoDialog *dlg = new ExpoDialog(this, *model, &ed, generalSettings, firmware, inputName, rawSourceFilteredModel, rawSwitchFilteredModel, - curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel); + ExpoDialog *dlg = new ExpoDialog(this, *model, &ed, generalSettings, firmware, inputName, sharedItemModels); if (dlg->exec()) { model->expoData[index] = ed; if (firmware->getCapability(VirtualInputs)) @@ -732,18 +724,31 @@ int InputsPanel::getInputIndexFromSelected() return idx; } +void InputsPanel::connectItemModelEvents(const int id) +{ + AbstractDynamicItemModel * itemModel = qobject_cast(sharedItemModels->getItemModel(id)); + if (itemModel) { + connect(itemModel, &AbstractDynamicItemModel::aboutToBeUpdated, this, &InputsPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &AbstractDynamicItemModel::updateComplete, this, &InputsPanel::onItemModelUpdateComplete); + } +} + void InputsPanel::onItemModelAboutToBeUpdated() { lock = true; + modelsUpdateCnt++; } void InputsPanel::onItemModelUpdateComplete() { - update(); - lock = false; + modelsUpdateCnt--; + if (modelsUpdateCnt < 1) { + update(); + lock = false; + } } void InputsPanel::updateItemModels() { - sharedItemModels->update(AbstractItemModel::InputsUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_Inputs); } diff --git a/companion/src/modeledit/inputs.h b/companion/src/modeledit/inputs.h index 8b2c6bcef..9b360ec19 100644 --- a/companion/src/modeledit/inputs.h +++ b/companion/src/modeledit/inputs.h @@ -25,7 +25,7 @@ #include "mixerslistwidget.h" #include "modelprinter.h" -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; constexpr char MIMETYPE_EXPO[] = "application/x-companion-expo"; @@ -35,7 +35,7 @@ class InputsPanel : public ModelPanel Q_OBJECT public: - InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~InputsPanel(); virtual void update(); @@ -72,13 +72,8 @@ class InputsPanel : public ModelPanel ModelPrinter modelPrinter; int selectedIdx; int inputIdx; - ItemModelsFactory *sharedItemModels; - FilteredItemModel *rawSourceFilteredModel; - FilteredItemModel *rawSwitchFilteredModel; - FilteredItemModel *curveFilteredModel; - FilteredItemModel *gvarFilteredModel; - FilteredItemModel *crTypeFilteredModel; - FilteredItemModel *crFuncFilteredModel; + CompoundItemModelFactory *sharedItemModels; + int modelsUpdateCnt; int getExpoIndex(unsigned int dch); bool gm_insertExpo(int idx); @@ -100,6 +95,7 @@ class InputsPanel : public ModelPanel int getIndexFromSelected(); int getInputIndexFromSelected(); void updateItemModels(); + void connectItemModelEvents(const int id); }; #endif // _INPUTS_H_ diff --git a/companion/src/modeledit/logicalswitches.cpp b/companion/src/modeledit/logicalswitches.cpp index dc4755f53..499df9e49 100644 --- a/companion/src/modeledit/logicalswitches.cpp +++ b/companion/src/modeledit/logicalswitches.cpp @@ -19,21 +19,25 @@ */ #include "logicalswitches.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #include "helpers.h" #include LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels): + CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), sharedItemModels(sharedItemModels), selectedIndex(0), modelsUpdateCnt(0) { - FILTEREDITEMMODEL(rawSwitchFilteredModel, LogicalSwitchesPanel, RawSwitchId, RawSwitch::LogicalSwitchesContext) + rawSwitchFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), + RawSwitch::LogicalSwitchesContext); + connectItemModelEvents(rawSwitchFilteredModel); + const int srcGroups = firmware->getCapability(GvarsInCS) ? 0 : (RawSource::AllSourceGroups & ~RawSource::GVarsGroup); - FILTEREDITEMMODEL(rawSourceFilteredModel, LogicalSwitchesPanel, RawSourceId, srcGroups) + rawSourceFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), srcGroups); + connectItemModelEvents(rawSourceFilteredModel); lsCapability = firmware->getCapability(LogicalSwitches); lsCapabilityExt = firmware->getCapability(LogicalSwitchesExt); @@ -642,7 +646,13 @@ void LogicalSwitchesPanel::swapData(int idx1, int idx2) void LogicalSwitchesPanel::updateItemModels() { lock = true; - sharedItemModels->update(AbstractItemModel::LogicalSwitchesUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_LogicalSwitches); +} + +void LogicalSwitchesPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &LogicalSwitchesPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &LogicalSwitchesPanel::onItemModelUpdateComplete); } void LogicalSwitchesPanel::onItemModelAboutToBeUpdated() diff --git a/companion/src/modeledit/logicalswitches.h b/companion/src/modeledit/logicalswitches.h index 05b683b49..f1c2f8397 100644 --- a/companion/src/modeledit/logicalswitches.h +++ b/companion/src/modeledit/logicalswitches.h @@ -24,7 +24,7 @@ #include "modeledit.h" #include "radiodata.h" -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; class TimerEdit; @@ -35,7 +35,7 @@ class LogicalSwitchesPanel : public ModelPanel Q_OBJECT public: - LogicalSwitchesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + LogicalSwitchesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~LogicalSwitchesPanel(); virtual void update(); @@ -74,7 +74,7 @@ class LogicalSwitchesPanel : public ModelPanel QDoubleSpinBox * dsbDelay[CPN_MAX_LOGICAL_SWITCHES]; QComboBox * cbSource1[CPN_MAX_LOGICAL_SWITCHES]; QComboBox * cbSource2[CPN_MAX_LOGICAL_SWITCHES]; - ItemModelsFactory * sharedItemModels; + CompoundItemModelFactory * sharedItemModels; FilteredItemModel * rawSwitchFilteredModel; FilteredItemModel * rawSourceFilteredModel; int selectedIndex; @@ -89,6 +89,7 @@ class LogicalSwitchesPanel : public ModelPanel bool moveUpAllowed() const; int modelsUpdateCnt; void updateItemModels(); + void connectItemModelEvents(const FilteredItemModel * itemModel); }; #endif // _LOGICALSWITCHES_H_ diff --git a/companion/src/modeledit/mixerdialog.cpp b/companion/src/modeledit/mixerdialog.cpp index 819649698..67b01f8f6 100644 --- a/companion/src/modeledit/mixerdialog.cpp +++ b/companion/src/modeledit/mixerdialog.cpp @@ -21,13 +21,11 @@ #include "mixerdialog.h" #include "ui_mixerdialog.h" #include "radiodata.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #include "helpers.h" MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, GeneralSettings & generalSettings, Firmware * firmware, - FilteredItemModel * rawSourceFilteredModel, FilteredItemModel * rawSwitchFilteredModel, - FilteredItemModel * curveFilteredModel, FilteredItemModel * gvarFilteredModel, - FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel) : + CompoundItemModelFactory * sharedItemModels) : QDialog(parent), ui(new Ui::MixerDialog), model(model), @@ -38,6 +36,9 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, { ui->setupUi(this); + dialogFilteredItemModels = new FilteredItemModelFactory(); + int id; + QRegExp rx(CHAR_FOR_NAMES_REGEX); QLabel * lb_fp[CPN_MAX_FLIGHT_MODES] = {ui->lb_FP0, ui->lb_FP1, ui->lb_FP2, ui->lb_FP3, ui->lb_FP4, ui->lb_FP5, ui->lb_FP6, ui->lb_FP7, ui->lb_FP8 }; QCheckBox * tmp[CPN_MAX_FLIGHT_MODES] = {ui->cb_FP0, ui->cb_FP1, ui->cb_FP2, ui->cb_FP3, ui->cb_FP4, ui->cb_FP5, ui->cb_FP6, ui->cb_FP7, ui->cb_FP8 }; @@ -47,15 +48,20 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, this->setWindowTitle(tr("DEST -> %1").arg(RawSource(SOURCE_TYPE_CH, md->destCh - 1).toString(&model, &generalSettings))); - ui->sourceCB->setModel(rawSourceFilteredModel); + id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), + ((RawSource::InputSourceGroups | RawSource::ScriptsGroup) & ~ RawSource::NoneGroup)), + "RawSource"); + ui->sourceCB->setModel(dialogFilteredItemModels->getItemModel(id)); ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(md->srcRaw.toValue())); int limit = firmware->getCapability(OffsetWeight); gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, md->weight, model, 100, -limit, limit); gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, md->sOffset, model, 0, -limit, limit); + curveRefFilteredItemModels = new CurveRefFilteredFactory(sharedItemModels, + firmware->getCapability(HasMixerExpo) ? 0 : FilteredItemModel::PositiveFilter); curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, md->curve, model, - curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel, this); + curveRefFilteredItemModels, this); ui->MixDR_CB->setChecked(md->noExpo == 0); @@ -107,7 +113,9 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, } } - ui->switchesCB->setModel(rawSwitchFilteredModel); + id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), + RawSwitch::MixesContext), "RawSwitch"); + ui->switchesCB->setModel(dialogFilteredItemModels->getItemModel(id)); ui->switchesCB->setCurrentIndex(ui->switchesCB->findData(md->swtch.toValue())); ui->warningCB->setCurrentIndex(md->mixWarn); ui->mltpxCB->setCurrentIndex(md->mltpx); @@ -153,6 +161,7 @@ MixerDialog::~MixerDialog() delete ui; delete gvWeightGroup; delete gvOffsetGroup; + delete dialogFilteredItemModels; } void MixerDialog::changeEvent(QEvent *e) diff --git a/companion/src/modeledit/mixerdialog.h b/companion/src/modeledit/mixerdialog.h index 1beb3b220..04ff1aeaf 100644 --- a/companion/src/modeledit/mixerdialog.h +++ b/companion/src/modeledit/mixerdialog.h @@ -25,7 +25,9 @@ #include "eeprominterface.h" class GVarGroup; -class FilteredItemModel; +class CompoundItemModelFactory; +class FilteredItemModelFactory; +class CurveRefFilteredFactory; class CurveReferenceUIManager; namespace Ui { @@ -36,8 +38,7 @@ class MixerDialog : public QDialog { Q_OBJECT public: MixerDialog(QWidget *parent, ModelData & model, MixData *mixdata, GeneralSettings & generalSettings, Firmware * firmware, - FilteredItemModel * rawSourceFilteredModel, FilteredItemModel * rawSwitchFilteredModel, FilteredItemModel * curveFilteredModel, - FilteredItemModel * gvarFilteredModel, FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel); + CompoundItemModelFactory * sharedItemModels); ~MixerDialog(); protected: @@ -62,6 +63,8 @@ class MixerDialog : public QDialog { GVarGroup * gvOffsetGroup; CurveReferenceUIManager * curveGroup; QCheckBox * cb_fp[CPN_MAX_FLIGHT_MODES]; + FilteredItemModelFactory *dialogFilteredItemModels; + CurveRefFilteredFactory *curveRefFilteredItemModels; }; #endif // _MIXERDIALOG_H_ diff --git a/companion/src/modeledit/mixes.cpp b/companion/src/modeledit/mixes.cpp index 5bcc69d30..3d95bd04d 100644 --- a/companion/src/modeledit/mixes.cpp +++ b/companion/src/modeledit/mixes.cpp @@ -20,21 +20,20 @@ #include "mixes.h" #include "helpers.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" -MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): +MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), mixInserted(false), highlightedSource(0), modelPrinter(firmware, generalSettings, model), - sharedItemModels(sharedItemModels) + sharedItemModels(sharedItemModels), + modelsUpdateCnt(0) { - FILTEREDITEMMODEL(rawSourceFilteredModel, MixesPanel, RawSourceId,((RawSource::InputSourceGroups | RawSource::ScriptsGroup) & ~ RawSource::NoneGroup)) - FILTEREDITEMMODEL(rawSwitchFilteredModel, MixesPanel, RawSwitchId, RawSwitch::MixesContext) - FILTEREDITEMMODEL(curveFilteredModel, MixesPanel, CurveId, firmware->getCapability(HasMixerExpo) ? 0 : FilteredItemModel::PositiveFilter) - FILTEREDITEMMODELNOFLAGS(gvarFilteredModel, MixesPanel, GVarRefId) - FILTEREDITEMMODELNOFLAGS(crTypeFilteredModel, MixesPanel, CurveRefTypeId) - FILTEREDITEMMODELNOFLAGS(crFuncFilteredModel, MixesPanel, CurveRefFuncId) + connectItemModelEvents(AbstractItemModel::IMID_RawSource); + connectItemModelEvents(AbstractItemModel::IMID_RawSwitch); + connectItemModelEvents(AbstractItemModel::IMID_Curve); + connectItemModelEvents(AbstractItemModel::IMID_GVarRef); QGridLayout * mixesLayout = new QGridLayout(this); @@ -69,12 +68,6 @@ MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & gen MixesPanel::~MixesPanel() { - delete rawSourceFilteredModel; - delete rawSwitchFilteredModel; - delete curveFilteredModel; - delete gvarFilteredModel; - delete crTypeFilteredModel; - delete crFuncFilteredModel; } void MixesPanel::update() @@ -190,8 +183,7 @@ void MixesPanel::gm_openMix(int index) MixData mixd(model->mixData[index]); - MixerDialog *dlg = new MixerDialog(this, *model, &mixd, generalSettings, firmware, rawSourceFilteredModel, rawSwitchFilteredModel, - curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel); + MixerDialog *dlg = new MixerDialog(this, *model, &mixd, generalSettings, firmware, sharedItemModels); if(dlg->exec()) { model->mixData[index] = mixd; emit modified(); @@ -547,13 +539,26 @@ void MixesPanel::clearMixes() } } +void MixesPanel::connectItemModelEvents(const int id) +{ + AbstractDynamicItemModel * itemModel = qobject_cast(sharedItemModels->getItemModel(id)); + if (itemModel) { + connect(itemModel, &AbstractDynamicItemModel::aboutToBeUpdated, this, &MixesPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &AbstractDynamicItemModel::updateComplete, this, &MixesPanel::onItemModelUpdateComplete); + } +} + void MixesPanel::onItemModelAboutToBeUpdated() { lock = true; + modelsUpdateCnt++; } void MixesPanel::onItemModelUpdateComplete() { - update(); - lock = false; + modelsUpdateCnt--; + if (modelsUpdateCnt < 1) { + update(); + lock = false; + } } diff --git a/companion/src/modeledit/mixes.h b/companion/src/modeledit/mixes.h index 211632a2e..c298df11a 100644 --- a/companion/src/modeledit/mixes.h +++ b/companion/src/modeledit/mixes.h @@ -26,7 +26,7 @@ #include "mixerdialog.h" #include "modelprinter.h" -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; class MixesPanel : public ModelPanel @@ -34,7 +34,7 @@ class MixesPanel : public ModelPanel Q_OBJECT public: - MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~MixesPanel(); virtual void update(); @@ -68,13 +68,8 @@ class MixesPanel : public ModelPanel bool mixInserted; unsigned int highlightedSource; ModelPrinter modelPrinter; - ItemModelsFactory *sharedItemModels; - FilteredItemModel *rawSourceFilteredModel; - FilteredItemModel *rawSwitchFilteredModel; - FilteredItemModel *curveFilteredModel; - FilteredItemModel *gvarFilteredModel; - FilteredItemModel *crTypeFilteredModel; - FilteredItemModel *crFuncFilteredModel; + CompoundItemModelFactory *sharedItemModels; + int modelsUpdateCnt; int getMixerIndex(unsigned int dch); bool gm_insertMix(int idx); @@ -86,6 +81,7 @@ class MixesPanel : public ModelPanel void setSelectedByMixList(QList list); void AddMixerLine(int dest); QString getMixerText(int dest, bool newChannel); + void connectItemModelEvents(const int id); }; #endif // _MIXES_H_ diff --git a/companion/src/modeledit/modeledit.cpp b/companion/src/modeledit/modeledit.cpp index 569b64e33..3577be7ae 100644 --- a/companion/src/modeledit/modeledit.cpp +++ b/companion/src/modeledit/modeledit.cpp @@ -33,7 +33,7 @@ #include "customfunctions.h" #include "telemetry.h" #include "appdata.h" -#include "rawitemdatamodels.h" +#include "compounditemmodels.h" ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmware * firmware) : QDialog(parent), @@ -53,17 +53,17 @@ ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmw GeneralSettings &generalSettings = radioData.generalSettings; ModelData &model = radioData.models[modelId]; - sharedItemModels = new ItemModelsFactory(&generalSettings, &model); - sharedItemModels->addItemModel(AbstractItemModel::RawSourceId); - sharedItemModels->addItemModel(AbstractItemModel::RawSwitchId); - sharedItemModels->addItemModel(AbstractItemModel::CurveId); - sharedItemModels->addItemModel(AbstractItemModel::GVarRefId); - sharedItemModels->addItemModel(AbstractItemModel::ThrSourceId); - sharedItemModels->addItemModel(AbstractItemModel::CustomFuncActionId); - sharedItemModels->addItemModel(AbstractItemModel::CustomFuncResetParamId); - sharedItemModels->addItemModel(AbstractItemModel::TeleSourceId); - sharedItemModels->addItemModel(AbstractItemModel::CurveRefTypeId); - sharedItemModels->addItemModel(AbstractItemModel::CurveRefFuncId); + sharedItemModels = new CompoundItemModelFactory(&generalSettings, &model); + sharedItemModels->addItemModel(AbstractItemModel::IMID_RawSource); + sharedItemModels->addItemModel(AbstractItemModel::IMID_RawSwitch); + sharedItemModels->addItemModel(AbstractItemModel::IMID_Curve); + sharedItemModels->addItemModel(AbstractItemModel::IMID_GVarRef); + sharedItemModels->addItemModel(AbstractItemModel::IMID_ThrSource); + sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncAction); + sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncResetParam); + sharedItemModels->addItemModel(AbstractItemModel::IMID_TeleSource); + sharedItemModels->addItemModel(AbstractItemModel::IMID_CurveRefType); + sharedItemModels->addItemModel(AbstractItemModel::IMID_CurveRefFunc); s1.report("Init"); diff --git a/companion/src/modeledit/modeledit.h b/companion/src/modeledit/modeledit.h index 99337f862..0bd4af9d8 100644 --- a/companion/src/modeledit/modeledit.h +++ b/companion/src/modeledit/modeledit.h @@ -25,7 +25,7 @@ #include "genericpanel.h" class RadioData; -class ItemModelsFactory; +class CompoundItemModelFactory; namespace Ui { class ModelEdit; @@ -63,7 +63,7 @@ class ModelEdit : public QDialog RadioData &radioData; Firmware *firmware; QVector panels; - ItemModelsFactory *sharedItemModels; + CompoundItemModelFactory *sharedItemModels; void addTab(GenericPanel * panel, QString text); void launchSimulation(); diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index d3f58322e..25cd576ec 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -22,7 +22,7 @@ #include "ui_setup.h" #include "ui_setup_timer.h" #include "ui_setup_module.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #include "appdata.h" #include "modelprinter.h" #include "multiprotocols.h" @@ -38,8 +38,7 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge ui(new Ui::Timer) { ui->setupUi(this); - connect(rawSwitchFilteredModel, &FilteredItemModel::aboutToBeUpdated, this, &TimerPanel::onItemModelAboutToBeUpdated); - connect(rawSwitchFilteredModel, &FilteredItemModel::updateComplete, this, &TimerPanel::onItemModelUpdateComplete); + connectItemModelEvents(rawSwitchFilteredModel); lock = true; @@ -173,6 +172,12 @@ void TimerPanel::on_name_editingFinished() } } +void TimerPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TimerPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &TimerPanel::onItemModelUpdateComplete); +} + void TimerPanel::onItemModelAboutToBeUpdated() { lock = true; @@ -993,14 +998,18 @@ void ModulePanel::onClearAccessRxClicked() /******************************************************************************/ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels) : + CompoundItemModelFactory * sharedItemModels) : ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Setup), sharedItemModels(sharedItemModels) { ui->setupUi(this); - FILTEREDITEMMODEL(rawSwitchFilteredModel, SetupPanel, RawSwitchId, RawSwitch::TimersContext) - FILTEREDITEMMODELNOFLAGS(thrSourceFilteredModel, SetupPanel, ThrSourceId) + + rawSwitchFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), RawSwitch::TimersContext); + connectItemModelEvents(rawSwitchFilteredModel); + + thrSourceFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_ThrSource)); + connectItemModelEvents(thrSourceFilteredModel); Board::Type board = firmware->getBoard(); @@ -1755,6 +1764,12 @@ void SetupPanel::onTimerNameChanged() updateItemModels(); } +void SetupPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &SetupPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &SetupPanel::onItemModelUpdateComplete); +} + void SetupPanel::onItemModelAboutToBeUpdated() { } @@ -1765,5 +1780,5 @@ void SetupPanel::onItemModelUpdateComplete() void SetupPanel::updateItemModels() { - sharedItemModels->update(AbstractItemModel::TimersUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_Timers); } diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index a4b7e7f81..c931f9dd7 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -26,7 +26,7 @@ constexpr char MIMETYPE_TIMER[] = "application/x-companion-timer"; -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; namespace Ui { @@ -61,6 +61,7 @@ class TimerPanel : public ModelPanel private: TimerData & timer; Ui::Timer * ui; + void connectItemModelEvents(const FilteredItemModel * itemModel); }; class ModulePanel : public ModelPanel @@ -132,7 +133,7 @@ class SetupPanel : public ModelPanel Q_OBJECT public: - SetupPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + SetupPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~SetupPanel(); virtual void update(); @@ -194,10 +195,11 @@ class SetupPanel : public ModelPanel bool moveTimerDownAllowed() const; bool moveTimerUpAllowed() const; void swapTimerData(int idx1, int idx2); - ItemModelsFactory * sharedItemModels; + CompoundItemModelFactory * sharedItemModels; FilteredItemModel * rawSwitchFilteredModel; FilteredItemModel * thrSourceFilteredModel; void updateItemModels(); - }; + void connectItemModelEvents(const FilteredItemModel * itemModel); +}; #endif // _SETUP_H_ diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index 3e1a5c49b..423e10e5d 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -27,12 +27,17 @@ #include -constexpr char FIM_NAME_RAWSOURCE[] {"Raw Source"}; -constexpr char FIM_NAME_TELEALLSRC[] {"Tele All Source"}; -constexpr char FIM_NAME_TELEPOSSRC[] {"Tele Pos Source"}; +constexpr char FIM_RAWSOURCE[] {"Raw Source"}; +constexpr char FIM_TELEALLSRC[] {"Tele All Source"}; +constexpr char FIM_TELEPOSSRC[] {"Tele Pos Source"}; +constexpr char FIM_SENSORTYPE[] {"Sensor.Type"}; +constexpr char FIM_SENSORFORMULA[] {"Sensor.Formula"}; +constexpr char FIM_SENSORCELLINDEX[] {"Sensor.CellIndex"}; +constexpr char FIM_SENSORUNIT[] {"Sensor.Unit"}; +constexpr char FIM_SENSORPRECISION[] {"Sensor.Precision"}; TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, - Firmware * firmware, const bool & parentLock, FilteredItemModelsFactory * localFilteredItemModels): + Firmware * firmware, const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetryCustomScreen), screen(screen), @@ -41,7 +46,7 @@ TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, { ui->setupUi(this); - FilteredItemModel * rawSourceFilteredModel = localFilteredItemModels->getItemModel(FIM_NAME_RAWSOURCE); + FilteredItemModel * rawSourceFilteredModel = panelFilteredItemModels->getItemModel(FIM_RAWSOURCE); connectItemModelEvents(rawSourceFilteredModel); for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) { @@ -321,24 +326,26 @@ void TelemetryCustomScreen::onItemModelUpdateComplete() TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, const bool & parentLock, - FilteredItemModelsFactory * localFilteredItemModels) : + FilteredItemModelFactory * panelFilteredItemModels) : ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetrySensor), sensor(sensor), - //lock(false), sensorIndex(sensorIndex), selectedIndex(0), sensorCapability(sensorCapability), modelsUpdateCnt(0), parentLock(parentLock) { + // TODO ui definition housekeeping + // - remove all predefined combobox lists leaving to code + // - use Auto types for consistency ui->setupUi(this); + FilteredItemModel * fltmdl; - FilteredItemModel * teleAllSrcFilteredModel = localFilteredItemModels->getItemModel(FIM_NAME_TELEALLSRC); - connectItemModelEvents(teleAllSrcFilteredModel); - FilteredItemModel * telePosSrcFilteredModel = localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC); - connectItemModelEvents(telePosSrcFilteredModel); + connectItemModelEvents(panelFilteredItemModels->getItemModel(FIM_TELEALLSRC)); + connectItemModelEvents(panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC)); + lock = true; ui->numLabel->setText(tr("TELE%1").arg(sensorIndex + 1)); ui->numLabel->setProperty("index", sensorIndex); ui->numLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); @@ -353,6 +360,9 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, connect(ui->numLabel, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(on_customContextMenuRequested(QPoint))); ui->id->setField(sensor.id, this); ui->instance->setField(sensor.instance, this); + ui->type->setModel(panelFilteredItemModels->getItemModel(FIM_SENSORTYPE)); + ui->formula->setModel(panelFilteredItemModels->getItemModel(FIM_SENSORFORMULA)); + ui->unit->setModel(panelFilteredItemModels->getItemModel(FIM_SENSORUNIT)); ui->ratio->setField(sensor.ratio, this); ui->offset->setField(sensor.offset, this); ui->autoOffset->setField(sensor.autoOffset, this); @@ -360,29 +370,29 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ui->logs->setField(sensor.logs, this); ui->persistent->setField(sensor.persistent, this); ui->onlyPositive->setField(sensor.onlyPositive, this); - ui->gpsSensor->setModel(telePosSrcFilteredModel); + fltmdl = panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC); + ui->gpsSensor->setModel(fltmdl); ui->gpsSensor->setField(sensor.gps, this); - ui->altSensor->setModel(telePosSrcFilteredModel); + ui->altSensor->setModel(fltmdl); ui->altSensor->setField(sensor.alt, this); - ui->ampsSensor->setModel(telePosSrcFilteredModel); + ui->ampsSensor->setModel(fltmdl); ui->ampsSensor->setField(sensor.amps, this); - ui->cellsSensor->setModel(telePosSrcFilteredModel); + ui->cellsSensor->setModel(fltmdl); ui->cellsSensor->setField(sensor.source, this); - ui->cellsIndex->addItem(tr("Lowest"), SensorData::TELEM_CELL_INDEX_LOWEST); - for (int i = SensorData::TELEM_CELL_INDEX_LOWEST + 1; i < SensorData::TELEM_CELL_INDEX_HIGHEST; i++) - ui->cellsIndex->addItem(tr("Cell %1").arg(i), i); - ui->cellsIndex->addItem(tr("Highest"), SensorData::TELEM_CELL_INDEX_HIGHEST); - ui->cellsIndex->addItem(tr("Delta"), SensorData::TELEM_CELL_INDEX_DELTA); + ui->cellsIndex->setModel(panelFilteredItemModels->getItemModel(FIM_SENSORCELLINDEX)); ui->cellsIndex->setField(sensor.index); - ui->source1->setModel(teleAllSrcFilteredModel); + fltmdl = panelFilteredItemModels->getItemModel(FIM_TELEALLSRC); + ui->source1->setModel(fltmdl); ui->source1->setField(sensor.sources[0], this); - ui->source2->setModel(teleAllSrcFilteredModel); + ui->source2->setModel(fltmdl); ui->source2->setField(sensor.sources[1], this); - ui->source3->setModel(teleAllSrcFilteredModel); + ui->source3->setModel(fltmdl); ui->source3->setField(sensor.sources[2], this); - ui->source4->setModel(teleAllSrcFilteredModel); + ui->source4->setModel(fltmdl); ui->source4->setField(sensor.sources[3], this); - ui->prec->setField(sensor.prec, 0, 2, false, "", this); + ui->prec->setField(sensor.prec, 0, 2, false, "", this); // TODO replace autoprecisioncombobox with autocombobox and filtereditemmodel + lock = false; + update(); } @@ -393,6 +403,7 @@ TelemetrySensorPanel::~TelemetrySensorPanel() void TelemetrySensorPanel::update() { + // TODO use bit mask bool isConfigurable = false; bool gpsFieldsDisplayed = false; bool cellsFieldsDisplayed = false; @@ -404,8 +415,8 @@ void TelemetrySensorPanel::update() lock = true; ui->name->setText(sensor.label); - ui->type->setCurrentIndex(sensor.type); - ui->unit->setCurrentIndex(sensor.unit); + ui->type->setCurrentIndex(ui->type->findData(sensor.type)); + ui->unit->setCurrentIndex(ui->unit->findData(sensor.unit)); ui->id->updateValue(); ui->instance->updateValue(); ui->ratio->updateValue(); @@ -431,7 +442,7 @@ void TelemetrySensorPanel::update() ui->originLabel->hide(); ui->origin->hide(); ui->formula->show(); - ui->formula->setCurrentIndex(sensor.formula); + ui->formula->setCurrentIndex(ui->formula->findData(sensor.formula)); isConfigurable = (sensor.formula < SensorData::TELEM_FORMULA_CELL); gpsFieldsDisplayed = (sensor.formula == SensorData::TELEM_FORMULA_DIST); cellsFieldsDisplayed = (sensor.formula == SensorData::TELEM_FORMULA_CELL); @@ -525,7 +536,7 @@ void TelemetrySensorPanel::on_name_editingFinished() void TelemetrySensorPanel::on_type_currentIndexChanged(int index) { if (!isLocked()) { - sensor.type = index; + sensor.type = ui->type->itemData(index).toInt(); update(); emit modified(); } @@ -534,7 +545,7 @@ void TelemetrySensorPanel::on_type_currentIndexChanged(int index) void TelemetrySensorPanel::on_formula_currentIndexChanged(int index) { if (!isLocked()) { - sensor.formula = index; + sensor.formula = ui->formula->itemData(index).toInt(); if (sensor.formula == SensorData::TELEM_FORMULA_CELL) { sensor.prec = 2; sensor.unit = SensorData::UNIT_VOLTS; @@ -554,7 +565,7 @@ void TelemetrySensorPanel::on_formula_currentIndexChanged(int index) void TelemetrySensorPanel::on_unit_currentIndexChanged(int index) { if (!isLocked()) { - sensor.unit = index; + sensor.unit = ui->unit->itemData(index).toInt(); if (sensor.unit == SensorData::UNIT_FAHRENHEIT) { sensor.prec = 0; } @@ -710,7 +721,7 @@ void TelemetrySensorPanel::onItemModelUpdateComplete() /******************************************************/ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels): + CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Telemetry), sharedItemModels(sharedItemModels), @@ -718,20 +729,37 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin { ui->setupUi(this); - localFilteredItemModels = new FilteredItemModelsFactory(); - int newid; + panelItemModels = new CompoundItemModelFactory(&generalSettings, &model); + panelFilteredItemModels = new FilteredItemModelFactory(); + int id; - newid = localFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::RawSourceId)), - FIM_NAME_RAWSOURCE); - connectItemModelEvents(localFilteredItemModels->getItemModel(newid)); + id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource)), + FIM_RAWSOURCE); + connectItemModelEvents(id); - newid = localFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::TeleSourceId)), - FIM_NAME_TELEALLSRC); - connectItemModelEvents(localFilteredItemModels->getItemModel(newid)); + id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_TeleSource)), + FIM_TELEALLSRC); + connectItemModelEvents(id); - newid = localFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::TeleSourceId), - FilteredItemModel::PositiveFilter), FIM_NAME_TELEPOSSRC); - connectItemModelEvents(localFilteredItemModels->getItemModel(newid)); + id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_TeleSource), + FilteredItemModel::PositiveFilter), + FIM_TELEPOSSRC); + connectItemModelEvents(id); + + id = panelItemModels->registerItemModel(SensorData::typeItemModel()); + panelFilteredItemModels->registerItemModel(new FilteredItemModel(panelItemModels->getItemModel(id)), FIM_SENSORTYPE); + + id = panelItemModels->registerItemModel(SensorData::formulaItemModel()); + panelFilteredItemModels->registerItemModel(new FilteredItemModel(panelItemModels->getItemModel(id)), FIM_SENSORFORMULA); + + id = panelItemModels->registerItemModel(SensorData::cellIndexItemModel()); + panelFilteredItemModels->registerItemModel(new FilteredItemModel(panelItemModels->getItemModel(id)), FIM_SENSORCELLINDEX); + + id = panelItemModels->registerItemModel(SensorData::unitItemModel()); + panelFilteredItemModels->registerItemModel(new FilteredItemModel(panelItemModels->getItemModel(id)), FIM_SENSORUNIT); + + id = panelItemModels->registerItemModel(SensorData::precisionItemModel()); + panelFilteredItemModels->registerItemModel(new FilteredItemModel(panelItemModels->getItemModel(id)), FIM_SENSORPRECISION); sensorCapability = firmware->getCapability(Sensors); if (sensorCapability > CPN_MAX_SENSORS) // TODO should be role of getCapability @@ -741,7 +769,7 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin model.frsky.usrProto = 1; } - ui->varioSource->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); + ui->varioSource->setModel(panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC)); ui->varioSource->setField(model.frsky.varioSource, this); ui->varioCenterSilent->setField(model.frsky.varioCenterSilent, this); ui->A1GB->hide(); @@ -749,7 +777,7 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin for (int i = 0; i < sensorCapability; ++i) { TelemetrySensorPanel * panel = new TelemetrySensorPanel(this, model.sensorData[i], i, sensorCapability, model, generalSettings, - firmware, lock, localFilteredItemModels); + firmware, lock, panelFilteredItemModels); ui->sensorsLayout->addWidget(panel); sensorPanels[i] = panel; connect(panel, SIGNAL(dataModified()), this, SLOT(on_dataModifiedSensor())); @@ -762,9 +790,9 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin } if (IS_TARANIS_X9(firmware->getBoard())) { - ui->voltsSource->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); + ui->voltsSource->setModel(panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC)); ui->voltsSource->setField(model.frsky.voltsSource, this); - ui->altitudeSource->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); + ui->altitudeSource->setModel(panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC)); ui->altitudeSource->setField(model.frsky.altitudeSource, this); } else { @@ -773,7 +801,7 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) { TelemetryCustomScreen * tab = new TelemetryCustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, lock, - localFilteredItemModels); + panelFilteredItemModels); ui->customScreens->addTab(tab, tr("Telemetry screen %1").arg(i + 1)); telemetryCustomScreens[i] = tab; connect(tab, &TelemetryCustomScreen::modified, this, &TelemetryPanel::onModified); @@ -787,7 +815,8 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin TelemetryPanel::~TelemetryPanel() { delete ui; - delete localFilteredItemModels; + delete panelFilteredItemModels; + delete panelItemModels; } void TelemetryPanel::update() @@ -839,7 +868,7 @@ void TelemetryPanel::setup() ui->rssiSourceLabel->show(); ui->rssiSourceLabel->setText(tr("Source")); - ui->rssiSourceCB->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); + ui->rssiSourceCB->setModel(panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC)); ui->rssiSourceCB->setField(model->rssiSource, this); ui->rssiSourceCB->show(); @@ -1053,12 +1082,13 @@ void TelemetryPanel::swapData(int idx1, int idx2) void TelemetryPanel::on_dataModifiedSensor() { - sharedItemModels->update(AbstractItemModel::TeleSensorsUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_TeleSensors); emit modified(); } -void TelemetryPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +void TelemetryPanel::connectItemModelEvents(const int id) { + FilteredItemModel * itemModel = panelFilteredItemModels->getItemModel(id); connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TelemetryPanel::onItemModelAboutToBeUpdated); connect(itemModel, &FilteredItemModel::updateComplete, this, &TelemetryPanel::onItemModelUpdateComplete); } diff --git a/companion/src/modeledit/telemetry.h b/companion/src/modeledit/telemetry.h index 51273d867..224f88020 100644 --- a/companion/src/modeledit/telemetry.h +++ b/companion/src/modeledit/telemetry.h @@ -23,7 +23,7 @@ #include "modeledit.h" #include "eeprominterface.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" constexpr char MIMETYPE_TELE_SENSOR[] {"application/x-companion-tele-sensor"}; @@ -42,7 +42,7 @@ class TelemetryCustomScreen: public ModelPanel public: TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware, - const bool & parentLock, FilteredItemModelsFactory * localFilteredItemModels); + const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels); ~TelemetryCustomScreen(); void update(); @@ -81,7 +81,7 @@ class TelemetrySensorPanel: public ModelPanel public: TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, const bool & parentLock, - FilteredItemModelsFactory * localFilteredItemModels); + FilteredItemModelFactory * panelFilteredItemModels); ~TelemetrySensorPanel(); void update(); @@ -116,7 +116,6 @@ class TelemetrySensorPanel: public ModelPanel private: Ui::TelemetrySensor * ui; SensorData & sensor; - //bool lock = false; int sensorIndex = 0; int selectedIndex = 0; int sensorCapability; @@ -136,7 +135,7 @@ class TelemetryPanel : public ModelPanel public: TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels); + CompoundItemModelFactory * sharedItemModels); virtual ~TelemetryPanel(); virtual void update(); @@ -171,14 +170,15 @@ class TelemetryPanel : public ModelPanel TelemetryCustomScreen *telemetryCustomScreens[4]; TelemetrySensorPanel *sensorPanels[CPN_MAX_SENSORS]; int sensorCapability; - ItemModelsFactory *sharedItemModels; - FilteredItemModelsFactory *localFilteredItemModels; + CompoundItemModelFactory *sharedItemModels; + CompoundItemModelFactory *panelItemModels; + FilteredItemModelFactory *panelFilteredItemModels; int modelsUpdateCnt; void setup(); void telBarUpdate(); void swapData(int idx1, int idx2); - void connectItemModelEvents(const FilteredItemModel * itemModel); + void connectItemModelEvents(const int id); inline bool isLocked() { return lock; } }; From 5457097b5d4d94581af9697e7b3070e1bc654b80 Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 8 Jan 2021 21:10:09 +1100 Subject: [PATCH 34/65] remove experimental files --- companion/src/datamodels/itemmodelfactory.cpp | 223 ------------------ companion/src/datamodels/itemmodelfactory.h | 75 ------ 2 files changed, 298 deletions(-) delete mode 100644 companion/src/datamodels/itemmodelfactory.cpp delete mode 100644 companion/src/datamodels/itemmodelfactory.h diff --git a/companion/src/datamodels/itemmodelfactory.cpp b/companion/src/datamodels/itemmodelfactory.cpp deleted file mode 100644 index b5b341afd..000000000 --- a/companion/src/datamodels/itemmodelfactory.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - * 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 "itemmodelfactory.h" - -#include "compounditemmodels.h" -#include "filtereditemmodels.h" - -#include "eeprominterface.h" -#include "generalsettings.h" -#include "modeldata.h" - -ItemModelFactory::ItemModelFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData) : - generalSettings(generalSettings), - modelData(modelData) -{ - firmware = getCurrentFirmware(); - board = new Boards(getCurrentBoard()); - boardType = getCurrentBoard(); -} - -ItemModelFactory::~ItemModelFactory() -{ - unregisterItemModels(); - delete board; -} - -void ItemModelFactory::addItemModel(const AbstractItemModel::ItemModelId id) -{ - switch (id) { - case AbstractItemModel::RawSourceId: - registerItemModel(new RawSourceItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::RawSwitchId: - registerItemModel(new RawSwitchItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::CurveId: - registerItemModel(new CurveItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::GVarRefId: - registerItemModel(new GVarReferenceItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::ThrSourceId: - registerItemModel(new ThrottleSourceItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::CustomFuncActionId: - registerItemModel(new CustomFuncActionItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::CustomFuncResetParamId: - registerItemModel(new CustomFuncResetParamItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::TeleSourceId: - registerItemModel(new TelemetrySourceItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::CurveRefTypeId: - registerItemModel(new CurveRefTypeItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::CurveRefFuncId: - registerItemModel(new CurveRefFuncItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - default: - qDebug() << "Error: unknown item model: id"; - break; - } -} - -void ItemModelFactory::registerItemModel(QAbstractItemModel * itemModel) -{ - if (itemModel && !getItemModel(itemModel->getId())) { - registeredItemModels.push_back(itemModel); - } -} - -void ItemModelFactory::unregisterItemModels() -{ - foreach (QAbstractItemModel *itemModel, registeredItemModels) { - delete itemModel; - } -} - -void ItemModelFactory::unregisterItemModel(const AbstractItemModel::ItemModelId id) -{ - QAbstractItemModel * itemModel = getItemModel(id); - if (itemModel) - delete itemModel; -} - -QAbstractItemModel * CompoundItemModelFactory::getItemModel(const AbstractItemModel::ItemModelId id) const -{ - foreach (AbstractItemModel * itemModel, registeredItemModels) { - if (itemModel->getId() == id) - return itemModel; - } - - return nullptr; -} - -void ItemModelFactory::update(const AbstractItemModel::UpdateTrigger trigger) -{ - foreach (AbstractItemModel * itemModel, registeredItemModels) { - itemModel->update(trigger); - } -} - -void ItemModelFactory::dumpAllItemModelContents() const -{ - foreach (AbstractItemModel * itemModel, registeredItemModels) { - AbstractItemModel::dumpItemModelContents(itemModel); - } -} - -// -// FilteredItemModelFactory -// - -FilteredItemModelFactory::FilteredItemModelFactory() -{ -} - -FilteredItemModelFactory::~FilteredItemModelFactory() -{ - unregisterItemModels(); -} - -int FilteredItemModelFactory::registerItemModel(FilteredItemModel * itemModel, QString name) -{ - if (itemModel) { - if (isItemModelRegistered(name)) - qDebug() << "Warning: item model already registered with name:" << name; - const int id = registeredItemModels.count(); - itemModel->setId(id); - itemModel->setName(name); - registeredItemModels.push_back(itemModel); - return id; - } - return 0; -} - -void FilteredItemModelFactory::unregisterItemModels() -{ - foreach (FilteredItemModel * itemModel, registeredItemModels) { - delete itemModel; - } -} - -void FilteredItemModelFactory::unregisterItemModel(const int id) -{ - FilteredItemModel * itemModel = getItemModel(id); - if (itemModel) - delete itemModel; -} - -bool FilteredItemModelFactory::isItemModelRegistered(const int id) const -{ - return getItemModel(id); -} - -bool FilteredItemModelFactory::isItemModelRegistered(const QString name) const -{ - return getItemModel(name); -} - -FilteredItemModel * FilteredItemModelFactory::getItemModel(const int id) const -{ - foreach (FilteredItemModel * itemModel, registeredItemModels) { - if (itemModel->getId() == id) - return itemModel; - } - - return nullptr; -} - -FilteredItemModel * FilteredItemModelFactory::getItemModel(const QString name) const -{ - foreach (FilteredItemModel * itemModel, registeredItemModels) { - if (itemModel->getName() == name) - return itemModel; - } - - return nullptr; -} - -void FilteredItemModelFactory::update() -{ - foreach (FilteredItemModel * itemModel, registeredItemModels) { - itemModel->update(); - } -} - -void FilteredItemModelFactory::dumpItemModelContents(const int id) const -{ - FilteredItemModel::dumpItemModelContents(getItemModel(id)); -} - -void FilteredItemModelFactory::dumpItemModelContents(const QString name) const -{ - FilteredItemModel::dumpItemModelContents(getItemModel(name)); -} - -void FilteredItemModelFactory::dumpAllItemModelContents() const -{ - foreach (FilteredItemModel * itemModel, registeredItemModels) { - FilteredItemModel::dumpItemModelContents(itemModel); - } -} - diff --git a/companion/src/datamodels/itemmodelfactory.h b/companion/src/datamodels/itemmodelfactory.h deleted file mode 100644 index 9f03b897b..000000000 --- a/companion/src/datamodels/itemmodelfactory.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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. - */ - -#ifndef ITEMMODELFACTORY_H -#define ITEMMODELFACTORY_H - -class GeneralSettings; -class ModelData; -class AbstractItemModel; -class FilteredItemModel; - -class ItemModelFactory -{ - public: - ItemModelFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData); - virtual ~ItemModelFactory(); - - void addItemModel(const AbstractItemModel::ItemModelId id); - void registerItemModel(AbstractItemModel * itemModel); - void unregisterItemModels(); - void unregisterItemModel(const AbstractItemModel::ItemModelId id); - AbstractItemModel * getItemModel(const AbstractItemModel::ItemModelId id) const; - void update(const AbstractItemModel::UpdateTrigger trigger = AbstractItemModel::SystemRefresh); - void dumpAllItemModelContents() const; - - protected: - const GeneralSettings * generalSettings; - const ModelData * modelData; - Firmware * firmware; - Boards * board; - Board::Type boardType; - QVector registeredItemModels; -}; - -class FilteredItemModelFactory -{ - public: - FilteredItemModelFactory(); - virtual ~FilteredItemModelFactory(); - - int registerItemModel(FilteredItemModel * itemModel, QString name); - void unregisterItemModels(); - void unregisterItemModel(const int id); - bool isItemModelRegistered(const int id) const; - bool isItemModelRegistered(const QString name) const; - FilteredItemModel * getItemModel(const int id) const; - FilteredItemModel * getItemModel(const QString name) const; - void update(); - - void dumpItemModelContents(const int id) const; - void dumpItemModelContents(const QString name) const; - void dumpAllItemModelContents() const; - - protected: - QVector registeredItemModels; -}; - -#endif // ITEMMODELFACTORY_H From 4328055e2a3fbfe6c7086bc02a76cb0999a1bf25 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 8 Jan 2021 16:57:14 +0100 Subject: [PATCH 35/65] Fix PL compile --- radio/src/translations/pl.h.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 8212ce84d..edfcee741 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -1142,6 +1142,10 @@ #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" #define TR_ABOUT_HARDWARE_3 "Współautor firmware" #else + #define TR_ABOUT_HARDWARE_1 "Brent Nelson" + #define TR_ABOUT_HARDWARE_2 "Sky9x designer/výrobce" + #define TR_ABOUT_HARDWARE_3 "" +#endif #define TR_ABOUT_PARENTS_1 "Projekty macierzyste" #define TR_ABOUT_PARENTS_2 TR("Ersky9x (Mike Blandford)", "Ersky9x (Mike Blandford)") From 6d2b76a5ed1a77d071896533f0aac2a12e43e99c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 8 Jan 2021 18:25:48 +0100 Subject: [PATCH 36/65] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 11971219f..3646b447b 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2080,3 +2080,5 @@ Carl Payne Adam Sosnowski Гасак Сергей SaltCtyRacingFPV +Georg Bernhardt +James Mildenhall From 53ec5001c2f5d7de3c595331572bb5d5f05fdca8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 9 Jan 2021 12:43:04 +0100 Subject: [PATCH 37/65] Update CREDITS.txt --- CREDITS.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 3646b447b..7d02bf7d4 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2082,3 +2082,8 @@ Adam Sosnowski SaltCtyRacingFPV Georg Bernhardt James Mildenhall +Dave Plummer +Ivan Mokris +Patrick Daniels +Stuart Olson +Cory Znebel From c2b9f2535b73ab86ed8e37295a9476c7ff6365aa Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 9 Jan 2021 19:51:29 +0100 Subject: [PATCH 38/65] Update CREDITS.txt --- CREDITS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 7d02bf7d4..676a24317 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2087,3 +2087,6 @@ Ivan Mokris Patrick Daniels Stuart Olson Cory Znebel +Andrey Yarovoy +Christian Pons +Burt Pickard-Richardson From fe475b11b1cf3cb963d9d4c92dc11df659368dd9 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 10 Jan 2021 10:55:22 +0100 Subject: [PATCH 39/65] Add some language testing to Travis --- radio/src/tests/switches.cpp | 2 +- tools/commit-tests.sh | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/radio/src/tests/switches.cpp b/radio/src/tests/switches.cpp index 4eae3ba5c..504fae354 100644 --- a/radio/src/tests/switches.cpp +++ b/radio/src/tests/switches.cpp @@ -106,7 +106,7 @@ TEST(evalLogicalSwitches, playFile) char filename[AUDIO_FILENAME_MAXLEN+1]; #if defined(EEPROM) -#define MODELNAME "MODEL01" +#define MODELNAME TR_MODEL "01" #else #define MODELNAME "Model00" #endif diff --git a/tools/commit-tests.sh b/tools/commit-tests.sh index 6c5642ccd..af2e7f040 100755 --- a/tools/commit-tests.sh +++ b/tools/commit-tests.sh @@ -88,7 +88,7 @@ fi if [[ " X9LITE ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X9LITE rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=X9LITE -DHELI=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=X9LITE -DHELI=YES -DGVARS=YES -DTRANSLATIONS=CZ "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio @@ -97,7 +97,7 @@ fi if [[ " X9LITES X9LITE ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X9LITES rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=X9LITES -DHELI=YES -DGVARS=YES -DMULTIMODULE=NO "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=X9LITES -DHELI=YES -DGVARS=YES -DMULTIMODULE=NO -DTRANSLATIONS=DE "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio @@ -106,7 +106,7 @@ fi if [[ " X7 ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X7 rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=X7 -DHELI=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=X7 -DHELI=YES -DGVARS=YES -DTRANSLATIONS=ES "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio @@ -115,7 +115,7 @@ fi if [[ " X7ACCESS X7 ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X7 ACCESS rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=X7 -DPCBREV=ACCESS -DHELI=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=X7 -DPCBREV=ACCESS -DHELI=YES -DGVARS=YES -DTRANSLATIONS=FR "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio @@ -124,7 +124,7 @@ fi if [[ " T12 X7 ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on T12 rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=X7 -DPCBREV=T12 -DHELI=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=X7 -DPCBREV=T12 -DHELI=YES -DGVARS=YES -DTRANSLATIONS=IT "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio @@ -133,7 +133,7 @@ fi if [[ " TX12 X7 ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on TX12 rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=X7 -DPCBREV=TX12 -DHELI=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=X7 -DPCBREV=TX12 -DHELI=YES -DGVARS=YES -DTRANSLATIONS=PT "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio @@ -142,7 +142,7 @@ fi if [[ " XLITE ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X-Lite rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=XLITE -DHELI=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=XLITE -DHELI=YES -DGVARS=YES -DTRANSLATIONS=SK "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio @@ -151,7 +151,7 @@ fi if [[ " XLITES XLITE ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X-Lites rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=XLITES -DHELI=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=XLITES -DHELI=YES -DGVARS=YES -DTRANSLATIONS=SE "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio @@ -160,7 +160,7 @@ fi if [[ " X9D X9 ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X9D rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=X9D -DHELI=YES -DLUA=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=X9D -DHELI=YES -DLUA=YES -DGVARS=YES -DTRANSLATIONS=PL "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio @@ -169,7 +169,7 @@ fi if [[ " X9D+ X9 ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X9D+ rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=X9D+ -DHELI=YES -DLUA=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=X9D+ -DHELI=YES -DLUA=YES -DGVARS=YES -DTRANSLATIONS=HU "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio @@ -178,7 +178,7 @@ fi if [[ " X9D+2019 X9 ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X9D+ 2019 rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=X9D+ -DPCBREV=2019 -DHELI=YES -DLUA=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=X9D+ -DPCBREV=2019 -DHELI=YES -DLUA=YES -DGVARS=YES -DTRANSLATIONS=NL "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio From c15eea42b740d3f889adfd2aa7f899f5e372bfa1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 10 Jan 2021 11:02:35 +0100 Subject: [PATCH 40/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 676a24317..29c5b28c4 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2090,3 +2090,4 @@ Cory Znebel Andrey Yarovoy Christian Pons Burt Pickard-Richardson +Michal Mesaros From 070f81e528633ae7b289e3c1dd565a149a26bce1 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 10 Jan 2021 11:20:11 +0100 Subject: [PATCH 41/65] Move nightlies to 2.3.12 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d846e898..74e6ccca0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ project(OpenTX) set(VERSION_MAJOR "2") set(VERSION_MINOR "3") -set(VERSION_REVISION "11") +set(VERSION_REVISION "12") set(VERSION_SUFFIX $ENV{OPENTX_VERSION_SUFFIX}) set(VERSION_FAMILY ${VERSION_MAJOR}.${VERSION_MINOR}) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}${VERSION_SUFFIX}) From 7699634217e10e0fc988262f95badc8d3187af36 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 10 Jan 2021 18:08:53 +0100 Subject: [PATCH 42/65] Fix #8204 --- radio/src/gui/480x272/model_setup.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index a620b5685..cd9974ac5 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -577,7 +577,7 @@ void onModelAntennaSwitchConfirm(const char * result) MODULE_TYPE_ROWS(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE*/ \ MULTIMODULE_STATUS_ROWS(INTERNAL_MODULE) /* ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS, ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS */ \ MODULE_CHANNELS_ROWS(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS*/ \ - IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, IF_INTERNAL_MODULE_ON(IF_INTERNAL_MODULE_ON(isModuleRxNumAvailable(INTERNAL_MODULE) ? (uint8_t)2 : (uint8_t)1))), /* *ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE */\ + IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, MODULE_BIND_ROWS(INTERNAL_MODULE)), /* *ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE */\ IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM*/ \ MODULE_OPTION_ROW(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS */ \ MULTIMODULE_MODULE_ROWS(INTERNAL_MODULE) /* ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND */ \ @@ -1549,6 +1549,7 @@ bool menuModelSetup(event_t event) lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RECEIVER); int l_posHorz = menuHorizontalPosition; coord_t bindButtonPos = MODEL_SETUP_2ND_COLUMN; + // RXNUM if (isModuleRxNumAvailable(moduleIdx)) { lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], (l_posHorz==0 ? attr : 0) | LEADING0 | LEFT, 2); bindButtonPos += 40; @@ -1572,12 +1573,13 @@ bool menuModelSetup(event_t event) else if (attr) { l_posHorz += 1; } + // BIND - RANGE if (isModuleBindRangeAvailable(moduleIdx)) { drawButton(bindButtonPos, y, STR_MODULE_BIND, (moduleState[moduleIdx].mode == MODULE_MODE_BIND ? BUTTON_ON : BUTTON_OFF) | (l_posHorz==1 ? attr : 0)); if (isModuleRangeAvailable(moduleIdx)) { drawButton(bindButtonPos + 80, y, STR_MODULE_RANGE, (moduleState[moduleIdx].mode == MODULE_MODE_RANGECHECK ? BUTTON_ON : BUTTON_OFF) | (l_posHorz==2 ? attr : 0)); } - uint8_t newFlag = 0; + uint8_t newFlag = MODULE_MODE_NORMAL; #if defined(MULTIMODULE) if (getMultiBindStatus(moduleIdx) == MULTI_BIND_FINISHED) { setMultiBindStatus(moduleIdx, MULTI_NORMAL_OPERATION); From 54a926322b69e8884ffc7bf2b170148e94f5aba3 Mon Sep 17 00:00:00 2001 From: WismyYao Date: Mon, 11 Jan 2021 11:38:19 +0800 Subject: [PATCH 43/65] add option for s.port/f.port/f.port2 --- .../gui/480x272/model_receiver_options.cpp | 38 +++++++++++-------- .../common/stdlcd/model_receiver_options.cpp | 36 +++++++++++------- radio/src/translations.cpp | 1 + radio/src/translations.h | 1 + radio/src/translations/untranslated.h | 3 ++ 5 files changed, 49 insertions(+), 30 deletions(-) diff --git a/radio/src/gui/480x272/model_receiver_options.cpp b/radio/src/gui/480x272/model_receiver_options.cpp index 38e46728c..6f7a2a94f 100644 --- a/radio/src/gui/480x272/model_receiver_options.cpp +++ b/radio/src/gui/480x272/model_receiver_options.cpp @@ -47,8 +47,7 @@ enum { ITEM_RECEIVER_SETTINGS_PWM_RATE, ITEM_RECEIVER_SETTINGS_TELEMETRY, ITEM_RECEIVER_SETTINGS_TELEMETRY_25MW, - ITEM_RECEIVER_SETTINGS_SPORT_FPORT, - ITEM_RECEIVER_SETTINGS_FPORT2, + ITEM_RECEIVER_SETTINGS_FPORT, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2, ITEM_RECEIVER_SETTINGS_PINMAP_FIRST @@ -60,6 +59,16 @@ enum { #define CH_ENABLE_SPORT 4 #define CH_ENABLE_SBUS 5 +bool isFPort2Available(int portType) +{ + uint8_t receiverId = reusableBuffer.hardwareAndSettings.receiverSettings.receiverId; + if (!IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2) && portType == 2) + { + return false; + } + return true; +} + bool menuModelReceiverOptions(event_t event) { const int lim = (g_model.extendedLimits ? (512 * LIMIT_EXT_PERCENT / 100) : 512) * 2; @@ -82,8 +91,7 @@ bool menuModelReceiverOptions(event_t event) 0, // PWM rate isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // Telemetry IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_TELEMETRY_25MW, 0), - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0), - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), + IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0) || IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), 0 // channels ... @@ -171,21 +179,19 @@ bool menuModelReceiverOptions(event_t event) } break; - case ITEM_RECEIVER_SETTINGS_SPORT_FPORT: - lcdDrawText(MENUS_MARGIN_LEFT, y, "F.Port"); - reusableBuffer.hardwareAndSettings.receiverSettings.fport = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.fport, RECEIVER_OPTIONS_2ND_COLUMN, y, attr, event); - if (attr && checkIncDec_Ret) { - reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; - } - break; - - case ITEM_RECEIVER_SETTINGS_FPORT2: - lcdDrawText(MENUS_MARGIN_LEFT, y, "F.Port2"); - reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.fport2, RECEIVER_OPTIONS_2ND_COLUMN, y, attr, event); - if (attr && checkIncDec_Ret) { + case ITEM_RECEIVER_SETTINGS_FPORT: + { + lcdDrawText(0, y, "PORT"); + uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; + lcdDrawTextAtIndex(LCD_W/2, y, STR_PORT_TYPES, portType, attr); + portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isFPort2Available); + if (checkIncDec_Ret) { + reusableBuffer.hardwareAndSettings.receiverSettings.fport = portType & 0x01; + reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = (portType & 0x02) >> 1; reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; } break; + } case ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1: lcdDrawText(LCD_W/2, y+1, STR_MORE_OPTIONS_AVAILABLE, SMLSIZE|CENTERED); diff --git a/radio/src/gui/common/stdlcd/model_receiver_options.cpp b/radio/src/gui/common/stdlcd/model_receiver_options.cpp index de3547b75..cf47462d7 100644 --- a/radio/src/gui/common/stdlcd/model_receiver_options.cpp +++ b/radio/src/gui/common/stdlcd/model_receiver_options.cpp @@ -47,8 +47,7 @@ enum { ITEM_RECEIVER_SETTINGS_PWM_RATE, ITEM_RECEIVER_SETTINGS_TELEMETRY, ITEM_RECEIVER_SETTINGS_TELEMETRY_25MW, - ITEM_RECEIVER_SETTINGS_SPORT_FPORT, - ITEM_RECEIVER_SETTINGS_FPORT2, + ITEM_RECEIVER_SETTINGS_FPORT, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2, ITEM_RECEIVER_SETTINGS_PINMAP_FIRST @@ -60,6 +59,16 @@ enum { #define CH_ENABLE_SPORT 4 #define CH_ENABLE_SBUS 5 +bool isFPort2Available(int portType) +{ + uint8_t receiverId = reusableBuffer.hardwareAndSettings.receiverSettings.receiverId; + if (!IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2) && portType == 2) + { + return false; + } + return true; +} + void menuModelReceiverOptions(event_t event) { const int lim = (g_model.extendedLimits ? (512 * LIMIT_EXT_PERCENT / 100) : 512) * 2; @@ -82,8 +91,7 @@ void menuModelReceiverOptions(event_t event) 0, // PWM rate isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // Telemetry IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_TELEMETRY_25MW, 0), - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0), - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), + IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0) || IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), 0 // channels ... @@ -166,19 +174,19 @@ void menuModelReceiverOptions(event_t event) } break; - case ITEM_RECEIVER_SETTINGS_SPORT_FPORT: - reusableBuffer.hardwareAndSettings.receiverSettings.fport = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.fport, RECEIVER_OPTIONS_2ND_COLUMN, y, "F.Port", attr, event); - if (attr && checkIncDec_Ret) { - reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; - } - break; - - case ITEM_RECEIVER_SETTINGS_FPORT2: - reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.fport2, RECEIVER_OPTIONS_2ND_COLUMN, y, "F.Port2", attr, event); - if (attr && checkIncDec_Ret) { + case ITEM_RECEIVER_SETTINGS_FPORT: + { + lcdDrawText(0, y, "PORT"); + uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; + lcdDrawTextAtIndex(LCD_W/2, y, STR_PORT_TYPES, portType, attr); + portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isFPort2Available); + if (checkIncDec_Ret) { + reusableBuffer.hardwareAndSettings.receiverSettings.fport = portType & 0x01; + reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = (portType & 0x02) >> 1; reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; } break; + } case ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1: lcdDrawText(LCD_W/2, y+1, STR_MORE_OPTIONS_AVAILABLE, SMLSIZE|CENTERED); diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 48d01b4bc..2b7b86e75 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -81,6 +81,7 @@ ISTR(POTTYPES); ISTR(SLIDERTYPES); ISTR(ANTENNA_MODES); ISTR(SPORT_UPDATE_POWER_MODES); +ISTR(PORT_TYPES); #if defined(PCBSKY9X) && defined(REVX) ISTR(VOUTPUT_TYPE); diff --git a/radio/src/translations.h b/radio/src/translations.h index 73c843389..89923f35f 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -161,6 +161,7 @@ extern const char STR_R9M_PXX2_RF_PROTOCOLS[]; extern const char STR_DSM_PROTOCOLS[]; extern const char STR_MULTI_PROTOCOLS[]; extern const char STR_MULTI_POWER[]; +extern const char STR_PORT_TYPES[]; #if defined(AFHDS3) extern const char STR_AFHDS3_ONE_TO_ONE_TELEMETRY[]; diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index e0e6f4bef..f9ee2deb3 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -109,6 +109,9 @@ #define LEN_R9M_PXX2_RF_PROTOCOLS "\006" #define TR_R9M_PXX2_RF_PROTOCOLS "ACCESS""FCC\0 ""EU\0 ""Flex" +#define LEN_PORT_TYPES "\007" +#define TR_PORT_TYPES "S.PORT\0""F.PORT\0""F.PORT2" + #define LEN_R9M_REGION "\006" #define TR_R9M_REGION "FCC\0 ""EU\0 ""868MHz""915MHz" From 5a90459ae480b0987ab89c541ccf9eeb2b30ea13 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 11 Jan 2021 19:28:52 +0100 Subject: [PATCH 44/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 29c5b28c4..4a26ad645 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2091,3 +2091,4 @@ Andrey Yarovoy Christian Pons Burt Pickard-Richardson Michal Mesaros +Conrad Young From 7e9d882edbadaa3fa1c8f6686c68c145f474133b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 12 Jan 2021 14:57:24 +0100 Subject: [PATCH 45/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 4a26ad645..3136f7b6e 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2092,3 +2092,4 @@ Christian Pons Burt Pickard-Richardson Michal Mesaros Conrad Young +Martin Mathes From e769d87cb2e5673f049dc6ce4b51dde119f51d69 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 12 Jan 2021 18:26:27 +0100 Subject: [PATCH 46/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 3136f7b6e..da2703ccd 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2093,3 +2093,4 @@ Burt Pickard-Richardson Michal Mesaros Conrad Young Martin Mathes +Jens Rainer Hansen From 9dfe19b0db0f8cd5f7a0260be156897f012e1b06 Mon Sep 17 00:00:00 2001 From: Sjoer van der Ploeg Date: Tue, 12 Jan 2021 22:24:09 +0100 Subject: [PATCH 47/65] Fix deprecated function --- companion/src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp index 9d490da39..23c5f71b7 100644 --- a/companion/src/mainwindow.cpp +++ b/companion/src/mainwindow.cpp @@ -1585,7 +1585,7 @@ void MainWindow::updateWindowActions() QString scut; if (++count < 10) scut = tr("Alt+%1").arg(count); - QAction * act = addActToGroup(windowsListActions, "", "", "window_ptr", qVariantFromValue(win), QVariant(), scut); + QAction * act = addActToGroup(windowsListActions, "", "", "window_ptr", QVariant::fromValue(win), QVariant(), scut); act->setChecked(win == mdiArea->activeSubWindow()); updateWindowActionTitle(win, act); } From a71f47e5ded55d2d603d713b67a66067a5885831 Mon Sep 17 00:00:00 2001 From: WismyYao Date: Wed, 13 Jan 2021 09:49:06 +0800 Subject: [PATCH 48/65] cosmetic --- radio/src/gui/common/stdlcd/model_receiver_options.cpp | 2 +- radio/src/translations.cpp | 2 ++ radio/src/translations/cz.h.txt | 1 + radio/src/translations/de.h.txt | 1 + radio/src/translations/en.h.txt | 1 + radio/src/translations/es.h.txt | 1 + radio/src/translations/fi.h.txt | 1 + radio/src/translations/fr.h.txt | 1 + radio/src/translations/it.h.txt | 1 + radio/src/translations/nl.h.txt | 1 + radio/src/translations/pl.h.txt | 1 + radio/src/translations/pt.h.txt | 1 + radio/src/translations/se.h.txt | 1 + 13 files changed, 14 insertions(+), 1 deletion(-) diff --git a/radio/src/gui/common/stdlcd/model_receiver_options.cpp b/radio/src/gui/common/stdlcd/model_receiver_options.cpp index cf47462d7..345863331 100644 --- a/radio/src/gui/common/stdlcd/model_receiver_options.cpp +++ b/radio/src/gui/common/stdlcd/model_receiver_options.cpp @@ -176,7 +176,7 @@ void menuModelReceiverOptions(event_t event) case ITEM_RECEIVER_SETTINGS_FPORT: { - lcdDrawText(0, y, "PORT"); + lcdDrawText(0, y, STR_PROTOCOL); uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; lcdDrawTextAtIndex(LCD_W/2, y, STR_PORT_TYPES, portType, attr); portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isFPort2Available); diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 2b7b86e75..276332f8a 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -830,3 +830,5 @@ const char STR_ABOUT_PARENTS_3[] = TR_ABOUT_PARENTS_3; const char STR_ABOUT_PARENTS_4[] = TR_ABOUT_PARENTS_4; const char STR_AUTH_FAILURE[] = TR_AUTH_FAILURE; +const char STR_PROTOCOL[] = TR_PROTOCOL; + diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 2f42f7edd..3ec57a040 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -1221,6 +1221,7 @@ #define TR_JITTER_FILTER "ADC Filtr" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 1e85008b0..93ebebcdd 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -1227,6 +1227,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" // ---------------------------------------------------------------- #define ZSTR_VFR "VFR" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index ad49d6997..b61b07c33 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -1225,6 +1225,7 @@ #define TR_JITTER_FILTER "ADC filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index e89998d7f..4dc05fb59 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -1224,6 +1224,7 @@ #define TR_JITTER_FILTER "Filtro ADC" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltaje") #define TR_AUTH_FAILURE "Fallo " LCDW_128_480_LINEBREAK "autentificación" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index e18b51102..011fc3b52 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -1233,6 +1233,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index ffaeb38c6..e2e55f51a 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -1249,6 +1249,7 @@ #define TR_JITTER_FILTER "Filtre ADC" #define TR_RTC_CHECK TR("Vérif. RTC", "Vérif. pile RTC") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index c1e8c0be0..6c402a265 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -1242,6 +1242,7 @@ #define TR_JITTER_FILTER "Filtro ADC" #define TR_RTC_CHECK TR("Controllo RTC", "Controllo volt. RTC") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index fe5abbfe3..a0966e1a7 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -1234,6 +1234,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index edfcee741..ea5851e82 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -1241,6 +1241,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 412e3698a..db824fc31 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -1241,6 +1241,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 57cf99ba6..44687c1d1 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -1241,6 +1241,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" From 9779359421bd5182e31fe39abfce0f22217e7884 Mon Sep 17 00:00:00 2001 From: WismyYao Date: Wed, 13 Jan 2021 09:54:25 +0800 Subject: [PATCH 49/65] rename --- radio/src/translations/untranslated.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index f9ee2deb3..4039492bc 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -109,8 +109,8 @@ #define LEN_R9M_PXX2_RF_PROTOCOLS "\006" #define TR_R9M_PXX2_RF_PROTOCOLS "ACCESS""FCC\0 ""EU\0 ""Flex" -#define LEN_PORT_TYPES "\007" -#define TR_PORT_TYPES "S.PORT\0""F.PORT\0""F.PORT2" +#define LEN_PORT_TYPES "\014" +#define TR_PORT_TYPES "S.PORT\0 ""F.PORT\0 ""FBUS(FPORT2)" #define LEN_R9M_REGION "\006" #define TR_R9M_REGION "FCC\0 ""EU\0 ""868MHz""915MHz" From 2ac4e7b64e7bee57111c809fa2105c3f48dd9390 Mon Sep 17 00:00:00 2001 From: WismyYao Date: Wed, 13 Jan 2021 18:21:36 +0800 Subject: [PATCH 50/65] fix --- radio/src/gui/480x272/model_receiver_options.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/480x272/model_receiver_options.cpp b/radio/src/gui/480x272/model_receiver_options.cpp index 6f7a2a94f..8d69817d1 100644 --- a/radio/src/gui/480x272/model_receiver_options.cpp +++ b/radio/src/gui/480x272/model_receiver_options.cpp @@ -181,7 +181,7 @@ bool menuModelReceiverOptions(event_t event) case ITEM_RECEIVER_SETTINGS_FPORT: { - lcdDrawText(0, y, "PORT"); + lcdDrawText(0, y, STR_PROTOCOL); uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; lcdDrawTextAtIndex(LCD_W/2, y, STR_PORT_TYPES, portType, attr); portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isFPort2Available); From 10ea134ae63bf801447960fd28a32516eacc8169 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 13 Jan 2021 17:06:22 +0100 Subject: [PATCH 51/65] Make text translatable --- companion/src/firmwares/curvereference.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 449dc0266..64138107c 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -77,7 +77,7 @@ int CurveReference::getDefaultValue(const CurveRefType type, const bool isGVar) // static QString CurveReference::typeToString(const CurveRefType type) { - const QStringList strl = { "Diff", "Expo" , "Func", "Curve" }; + const QStringList strl = { tr("Diff"), tr("Expo") , tr("Func"), tr("Curve") }; int idx = (int)type; if (idx < 0 || idx >= strl.count()) From ab528f22e0518ea1eb559496a485bd3fcb9b4b78 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 13 Jan 2021 18:12:32 +0100 Subject: [PATCH 52/65] Fixes --- radio/src/gui/128x64/model_setup.cpp | 4 ++-- radio/src/gui/480x272/model_receiver_options.cpp | 15 +++++++-------- .../gui/common/stdlcd/model_receiver_options.cpp | 15 +++++++-------- radio/src/translations.cpp | 3 +-- radio/src/translations.h | 3 ++- radio/src/translations/cz.h.txt | 3 +-- radio/src/translations/de.h.txt | 5 ++--- radio/src/translations/en.h.txt | 3 +-- radio/src/translations/es.h.txt | 3 +-- radio/src/translations/fi.h.txt | 3 +-- radio/src/translations/fr.h.txt | 5 ++--- radio/src/translations/it.h.txt | 5 ++--- radio/src/translations/nl.h.txt | 5 ++--- radio/src/translations/pl.h.txt | 5 ++--- radio/src/translations/pt.h.txt | 5 ++--- radio/src/translations/se.h.txt | 5 ++--- radio/src/translations/untranslated.h | 4 ++-- 17 files changed, 39 insertions(+), 52 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 41af5720f..004737515 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -857,7 +857,7 @@ void menuModelSetup(event_t event) #if !defined(INTERNAL_MODULE_MULTI) case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE: { - lcdDrawTextAlignedLeft(y, INDENT TR_MODE); + lcdDrawText(INDENT_WIDTH, y, STR_MODE); #if defined(INTERNAL_MODULE_PXX1) lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_INTERNAL_MODULE_PROTOCOLS, g_model.moduleData[INTERNAL_MODULE].type, menuHorizontalPosition==0 ? attr : 0); if (isModuleXJT(INTERNAL_MODULE)) @@ -1073,7 +1073,7 @@ void menuModelSetup(event_t event) #if defined(AFHDS3) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS_PROTOCOL: - lcdDrawTextAlignedLeft(y, TR_PROTO); + lcdDrawText(INDENT_WIDTH, y, STR_PROTOCOL); lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_AFHDS3_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].subType, attr); if (attr && s_editMode > 0 && menuHorizontalPosition == 0) { CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].subType, AFHDS_SUBTYPE_FIRST, AFHDS_SUBTYPE_LAST); diff --git a/radio/src/gui/480x272/model_receiver_options.cpp b/radio/src/gui/480x272/model_receiver_options.cpp index 8d69817d1..60292b223 100644 --- a/radio/src/gui/480x272/model_receiver_options.cpp +++ b/radio/src/gui/480x272/model_receiver_options.cpp @@ -47,7 +47,7 @@ enum { ITEM_RECEIVER_SETTINGS_PWM_RATE, ITEM_RECEIVER_SETTINGS_TELEMETRY, ITEM_RECEIVER_SETTINGS_TELEMETRY_25MW, - ITEM_RECEIVER_SETTINGS_FPORT, + ITEM_RECEIVER_SETTINGS_SPORT_MODE, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2, ITEM_RECEIVER_SETTINGS_PINMAP_FIRST @@ -59,11 +59,10 @@ enum { #define CH_ENABLE_SPORT 4 #define CH_ENABLE_SBUS 5 -bool isFPort2Available(int portType) +bool isSPortModeAvailable(int mode) { uint8_t receiverId = reusableBuffer.hardwareAndSettings.receiverSettings.receiverId; - if (!IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2) && portType == 2) - { + if (mode == 2 && !IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2)) { return false; } return true; @@ -91,7 +90,7 @@ bool menuModelReceiverOptions(event_t event) 0, // PWM rate isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // Telemetry IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_TELEMETRY_25MW, 0), - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0) || IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), + uint8_t((IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT) || IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2)) ? 0 : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), 0 // channels ... @@ -179,12 +178,12 @@ bool menuModelReceiverOptions(event_t event) } break; - case ITEM_RECEIVER_SETTINGS_FPORT: + case ITEM_RECEIVER_SETTINGS_SPORT_MODE: { lcdDrawText(0, y, STR_PROTOCOL); uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; - lcdDrawTextAtIndex(LCD_W/2, y, STR_PORT_TYPES, portType, attr); - portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isFPort2Available); + lcdDrawTextAtIndex(LCD_W/2, y, STR_SPORT_MODES, portType, attr); + portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isSPortModeAvailable); if (checkIncDec_Ret) { reusableBuffer.hardwareAndSettings.receiverSettings.fport = portType & 0x01; reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = (portType & 0x02) >> 1; diff --git a/radio/src/gui/common/stdlcd/model_receiver_options.cpp b/radio/src/gui/common/stdlcd/model_receiver_options.cpp index 345863331..7dc6e3354 100644 --- a/radio/src/gui/common/stdlcd/model_receiver_options.cpp +++ b/radio/src/gui/common/stdlcd/model_receiver_options.cpp @@ -47,7 +47,7 @@ enum { ITEM_RECEIVER_SETTINGS_PWM_RATE, ITEM_RECEIVER_SETTINGS_TELEMETRY, ITEM_RECEIVER_SETTINGS_TELEMETRY_25MW, - ITEM_RECEIVER_SETTINGS_FPORT, + ITEM_RECEIVER_SETTINGS_SPORT_MODE, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2, ITEM_RECEIVER_SETTINGS_PINMAP_FIRST @@ -59,11 +59,10 @@ enum { #define CH_ENABLE_SPORT 4 #define CH_ENABLE_SBUS 5 -bool isFPort2Available(int portType) +bool isSPortModeAvailable(int mode) { uint8_t receiverId = reusableBuffer.hardwareAndSettings.receiverSettings.receiverId; - if (!IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2) && portType == 2) - { + if (mode == 2 && !IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2)) { return false; } return true; @@ -91,7 +90,7 @@ void menuModelReceiverOptions(event_t event) 0, // PWM rate isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // Telemetry IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_TELEMETRY_25MW, 0), - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0) || IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), + uint8_t((IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT) || IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2)) ? 0 : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), 0 // channels ... @@ -174,12 +173,12 @@ void menuModelReceiverOptions(event_t event) } break; - case ITEM_RECEIVER_SETTINGS_FPORT: + case ITEM_RECEIVER_SETTINGS_SPORT_MODE: { lcdDrawText(0, y, STR_PROTOCOL); uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; - lcdDrawTextAtIndex(LCD_W/2, y, STR_PORT_TYPES, portType, attr); - portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isFPort2Available); + lcdDrawTextAtIndex(LCD_W/2, y, STR_SPORT_MODES, portType, attr); + portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isSPortModeAvailable); if (checkIncDec_Ret) { reusableBuffer.hardwareAndSettings.receiverSettings.fport = portType & 0x01; reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = (portType & 0x02) >> 1; diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 276332f8a..455d6490d 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -81,7 +81,7 @@ ISTR(POTTYPES); ISTR(SLIDERTYPES); ISTR(ANTENNA_MODES); ISTR(SPORT_UPDATE_POWER_MODES); -ISTR(PORT_TYPES); +ISTR(SPORT_MODES); #if defined(PCBSKY9X) && defined(REVX) ISTR(VOUTPUT_TYPE); @@ -831,4 +831,3 @@ const char STR_ABOUT_PARENTS_4[] = TR_ABOUT_PARENTS_4; const char STR_AUTH_FAILURE[] = TR_AUTH_FAILURE; const char STR_PROTOCOL[] = TR_PROTOCOL; - diff --git a/radio/src/translations.h b/radio/src/translations.h index 89923f35f..a9131d8a5 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -161,7 +161,7 @@ extern const char STR_R9M_PXX2_RF_PROTOCOLS[]; extern const char STR_DSM_PROTOCOLS[]; extern const char STR_MULTI_PROTOCOLS[]; extern const char STR_MULTI_POWER[]; -extern const char STR_PORT_TYPES[]; +extern const char STR_SPORT_MODES[]; #if defined(AFHDS3) extern const char STR_AFHDS3_ONE_TO_ONE_TELEMETRY[]; @@ -965,6 +965,7 @@ extern const char STR_ABOUT_PARENTS_3[]; extern const char STR_ABOUT_PARENTS_4[]; extern const char STR_AUTH_FAILURE[]; +extern const char STR_PROTOCOL[]; #define CHR_HOUR TR_CHR_HOUR #define CHR_INPUT TR_CHR_INPUT diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 3ec57a040..ac4392881 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -441,7 +441,7 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Výstup" #endif -#define TR_PROTO INDENT "Protokol" +#define TR_PROTOCOL "Protokol" #define TR_PPMFRAME INDENT "PPM modulace" #define TR_REFRESHRATE TR(INDENT "Obnovit", INDENT "Obn. frekv.") #define STR_WARN_BATTVOLTAGE TR(INDENT "Výstup VBAT: ", INDENT "Varování: výstupní hodnota VBAT: ") @@ -1221,7 +1221,6 @@ #define TR_JITTER_FILTER "ADC Filtr" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 93ebebcdd..87e879fb5 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -443,8 +443,8 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO TR(INDENT "Protok.", INDENT "Protokoll") - #define TR_PPMFRAME INDENT "PPM-Frame" +#define TR_PROTOCOL TR("Protok.", "Protokoll") +#define TR_PPMFRAME INDENT "PPM-Frame" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") #define TR_WARN_5VOLTS "Warning: output level is 5 volts" @@ -1227,7 +1227,6 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" // ---------------------------------------------------------------- #define ZSTR_VFR "VFR" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index b61b07c33..bfe26a48a 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -442,7 +442,7 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO TR(INDENT "Proto", INDENT "Protocol") +#define TR_PROTOCOL TR("Proto", "Protocol") #define TR_PPMFRAME INDENT "PPM frame" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") @@ -1225,7 +1225,6 @@ #define TR_JITTER_FILTER "ADC filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 4dc05fb59..d97f8a516 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -441,7 +441,7 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO TR(INDENT"Proto",INDENT"Protocolo") +#define TR_PROTOCOL TR("Proto", "Protocolo") #define TR_PPMFRAME INDENT "Trama PPM" #define TR_REFRESHRATE TR(INDENT "Refresco", INDENT "Velocidad refresco") #define STR_WARN_BATTVOLTAGE TR(INDENT "Salida es VBAT: ", INDENT "Aviso: señal salida es VBAT: ") @@ -1224,7 +1224,6 @@ #define TR_JITTER_FILTER "Filtro ADC" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltaje") #define TR_AUTH_FAILURE "Fallo " LCDW_128_480_LINEBREAK "autentificación" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 011fc3b52..013d97c5f 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -455,7 +455,7 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO TR(INDENT "Proto", INDENT "Protocol") +#define TR_PROTOCOL TR("Proto", "Protocol") #define TR_PPMFRAME TR("PPM frame", INDENT "PPM frame") #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") @@ -1233,7 +1233,6 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index e2e55f51a..e7045e019 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -461,8 +461,8 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Sortie" #endif -#define TR_PROTO TR(INDENT "Proto.", INDENT "Protocole") - #define TR_PPMFRAME INDENT "Trame PPM" +#define TR_PROTOCOL TR("Proto.", "Protocole") +#define TR_PPMFRAME INDENT "Trame PPM" #define TR_REFRESHRATE INDENT "Période" #define STR_WARN_BATTVOLTAGE TR(INDENT "Signal est VBAT: ", INDENT "ATTENTION: signal est à VBAT: ") #define TR_WARN_5VOLTS "Warning: output level is 5 volts" @@ -1249,7 +1249,6 @@ #define TR_JITTER_FILTER "Filtre ADC" #define TR_RTC_CHECK TR("Vérif. RTC", "Vérif. pile RTC") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 6c402a265..99b51bf74 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -463,8 +463,8 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Uscita" #endif -#define TR_PROTO TR(INDENT "Protoc.", INDENT "Protocollo") - #define TR_PPMFRAME INDENT "Frame PPM" +#define TR_PROTOCOL TR("Protoc.", "Protocollo") +#define TR_PPMFRAME INDENT "Frame PPM" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Uscita VBAT: ", INDENT "Att.: livel. uscita VBAT: ") #define TR_WARN_5VOLTS "Attenzione: il livello d'uscita è 5 Volts" @@ -1242,7 +1242,6 @@ #define TR_JITTER_FILTER "Filtro ADC" #define TR_RTC_CHECK TR("Controllo RTC", "Controllo volt. RTC") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index a0966e1a7..31f730302 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -442,8 +442,8 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO TR(INDENT "Proto", INDENT "Protocol") - #define TR_PPMFRAME INDENT "PPM frame" +#define TR_PROTOCOL TR("Proto", "Protocol") +#define TR_PPMFRAME INDENT "PPM frame" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") #define TR_WARN_5VOLTS "Warning: output level is 5 volts" @@ -1234,7 +1234,6 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index ea5851e82..4991d22bb 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -462,8 +462,8 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Wyjście" #endif -#define TR_PROTO TR(INDENT "Proto", INDENT "Protokół") - #define TR_PPMFRAME INDENT "Ramka PPM" +#define TR_PROTOCOL TR("Proto", "Protokół") +#define TR_PPMFRAME INDENT "Ramka PPM" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") #define TR_WARN_5VOLTS "Warning: output level is 5 volts" @@ -1241,7 +1241,6 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index db824fc31..5543649e4 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -459,7 +459,7 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO INDENT"Proto" +#define TR_PROTOCOL "Proto" #define TR_PPMFRAME "PPM frame" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") @@ -1240,8 +1240,7 @@ #define TR_MENU_INVERT "Invert" #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") -#define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" +#define TR_AUTH_FAILURE "Auth-failure" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 44687c1d1..7f8000575 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -461,8 +461,8 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO TR(INDENT "Proto", INDENT "Protokoll") - #define TR_PPMFRAME INDENT "PPM-paket" +#define TR_PROTOCOL TR("Proto", "Protokoll") +#define TR_PPMFRAME INDENT "PPM-paket" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") #define TR_WARN_5VOLTS "Warning: output level is 5 volts" @@ -1241,7 +1241,6 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index 4039492bc..268eb2606 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -109,8 +109,8 @@ #define LEN_R9M_PXX2_RF_PROTOCOLS "\006" #define TR_R9M_PXX2_RF_PROTOCOLS "ACCESS""FCC\0 ""EU\0 ""Flex" -#define LEN_PORT_TYPES "\014" -#define TR_PORT_TYPES "S.PORT\0 ""F.PORT\0 ""FBUS(FPORT2)" +#define LEN_SPORT_MODES "\014" +#define TR_SPORT_MODES "S.PORT\0 ""F.PORT\0 ""FBUS(FPORT2)" #define LEN_R9M_REGION "\006" #define TR_R9M_REGION "FCC\0 ""EU\0 ""868MHz""915MHz" From 889c16b1f6a2f27934d1886789c1bac1a21ca2ef Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 13 Jan 2021 18:14:32 +0100 Subject: [PATCH 53/65] Fixes --- radio/src/gui/480x272/model_receiver_options.cpp | 2 +- radio/src/gui/common/stdlcd/model_receiver_options.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/480x272/model_receiver_options.cpp b/radio/src/gui/480x272/model_receiver_options.cpp index 60292b223..0a2e2e4e6 100644 --- a/radio/src/gui/480x272/model_receiver_options.cpp +++ b/radio/src/gui/480x272/model_receiver_options.cpp @@ -181,7 +181,7 @@ bool menuModelReceiverOptions(event_t event) case ITEM_RECEIVER_SETTINGS_SPORT_MODE: { lcdDrawText(0, y, STR_PROTOCOL); - uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; + uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | (reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1); lcdDrawTextAtIndex(LCD_W/2, y, STR_SPORT_MODES, portType, attr); portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isSPortModeAvailable); if (checkIncDec_Ret) { diff --git a/radio/src/gui/common/stdlcd/model_receiver_options.cpp b/radio/src/gui/common/stdlcd/model_receiver_options.cpp index 7dc6e3354..fe5affb15 100644 --- a/radio/src/gui/common/stdlcd/model_receiver_options.cpp +++ b/radio/src/gui/common/stdlcd/model_receiver_options.cpp @@ -176,7 +176,7 @@ void menuModelReceiverOptions(event_t event) case ITEM_RECEIVER_SETTINGS_SPORT_MODE: { lcdDrawText(0, y, STR_PROTOCOL); - uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; + uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | (reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1); lcdDrawTextAtIndex(LCD_W/2, y, STR_SPORT_MODES, portType, attr); portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isSPortModeAvailable); if (checkIncDec_Ret) { From 0ee0c83d6f5ce5585be82539c0f11c3fe64def0f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 13 Jan 2021 19:18:33 +0100 Subject: [PATCH 54/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index da2703ccd..281e77ace 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2094,3 +2094,4 @@ Michal Mesaros Conrad Young Martin Mathes Jens Rainer Hansen +Mark Trible From 6342305e0af231ae0e374855d781d951ff9b8b73 Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 14 Jan 2021 07:10:59 +1100 Subject: [PATCH 55/65] Apply PR #8214 --- companion/src/firmwares/curvereference.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 6ade9264e..926c2d2ae 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -82,7 +82,7 @@ int CurveReference::getDefaultValue(const CurveRefType type, const bool isGVar) // static QString CurveReference::typeToString(const CurveRefType type) { - const QStringList strl = { "Diff", "Expo" , "Func", "Curve" }; + const QStringList strl = { tr("Diff"), tr("Expo") , tr("Func"), tr("Curve") }; int idx = (int)type; if (idx < 0 || idx >= strl.count()) From 6ffed2c7776f6e2ff3bb88b9224f42a2ab64104d Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 14 Jan 2021 16:58:10 +0100 Subject: [PATCH 56/65] Properly space swich/pots check text --- radio/src/switches.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/radio/src/switches.cpp b/radio/src/switches.cpp index bf230850d..727b05096 100644 --- a/radio/src/switches.cpp +++ b/radio/src/switches.cpp @@ -663,6 +663,9 @@ void checkSwitches() } int x = SWITCH_WARNING_LIST_X; int y = SWITCH_WARNING_LIST_Y; +#if defined(COLORLCD) + lcdNextPos = SWITCH_WARNING_LIST_X; +#endif int numWarnings = 0; for (int i=0; i Date: Fri, 15 Jan 2021 10:25:57 +0100 Subject: [PATCH 57/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 281e77ace..d068a43f0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2095,3 +2095,4 @@ Conrad Young Martin Mathes Jens Rainer Hansen Mark Trible +Marc Dubois From c139c6eefdf64292fb6d55800c15be09d5e41b5d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 15 Jan 2021 15:22:39 +0100 Subject: [PATCH 58/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index d068a43f0..41ce9cbb0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2096,3 +2096,4 @@ Martin Mathes Jens Rainer Hansen Mark Trible Marc Dubois +Alan Wilkins From 1f01fa6d47b6c0674581d3580e9dc589cc2e6a85 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 15 Jan 2021 17:19:25 +0100 Subject: [PATCH 59/65] Radiomaster T8 (#8219) --- companion/src/CMakeLists.txt | 2 + companion/src/companion.qrc | 12 + companion/src/firmwares/boards.cpp | 16 ++ companion/src/firmwares/boards.h | 10 +- companion/src/firmwares/opentx/opentxeeprom.h | 1 + .../src/firmwares/opentx/opentxinterface.cpp | 23 ++ companion/src/images/simulator/T8/bottom.png | Bin 0 -> 16616 bytes .../src/images/simulator/T8/left-pagedn.png | Bin 0 -> 14872 bytes .../src/images/simulator/T8/left-pageup.png | Bin 0 -> 14853 bytes .../src/images/simulator/T8/left-rtn.png | Bin 0 -> 14883 bytes .../src/images/simulator/T8/left-sys.png | Bin 0 -> 14832 bytes companion/src/images/simulator/T8/left.png | Bin 0 -> 14620 bytes .../src/images/simulator/T8/right-dn.png | Bin 0 -> 15515 bytes .../src/images/simulator/T8/right-ent.png | Bin 0 -> 15472 bytes .../src/images/simulator/T8/right-mdl.png | Bin 0 -> 15552 bytes .../src/images/simulator/T8/right-up.png | Bin 0 -> 15620 bytes companion/src/images/simulator/T8/right.png | Bin 0 -> 15355 bytes companion/src/images/simulator/T8/top.png | Bin 0 -> 3878 bytes companion/src/simulation/CMakeLists.txt | 2 + companion/src/simulation/simulateduiwidget.h | 12 + .../src/simulation/simulateduiwidgetT8.cpp | 67 +++++ .../src/simulation/simulateduiwidgetT8.ui | 258 ++++++++++++++++++ companion/src/simulation/simulatorwidget.cpp | 3 + radio/src/CMakeLists.txt | 4 + radio/src/dataconstants.h | 6 +- radio/src/gui/128x64/lcd.h | 3 +- radio/src/gui/128x64/model_curve_edit.cpp | 2 +- radio/src/gui/128x64/model_input_edit.cpp | 2 +- radio/src/gui/128x64/model_mix_edit.cpp | 2 +- radio/src/gui/128x64/model_select.cpp | 6 +- radio/src/gui/128x64/model_setup.cpp | 97 +++++-- radio/src/gui/128x64/radio_diagkeys.cpp | 11 +- radio/src/gui/128x64/radio_setup.cpp | 26 +- radio/src/gui/128x64/view_about.cpp | 2 +- radio/src/gui/128x64/view_channels.cpp | 7 +- radio/src/gui/128x64/view_main.cpp | 15 +- radio/src/gui/128x64/view_statistics.cpp | 12 +- .../src/gui/common/stdlcd/radio_hardware.cpp | 54 ++-- .../src/gui/common/stdlcd/view_telemetry.cpp | 2 +- radio/src/gui/navigation/navigation_x7.cpp | 29 +- radio/src/keys.h | 5 + radio/src/lua/CMakeLists.txt | 1 + radio/src/lua/api_general.cpp | 12 +- radio/src/main.cpp | 4 + radio/src/opentx.cpp | 19 +- radio/src/opentx.h | 18 ++ radio/src/options.h | 3 + radio/src/pulses/modules_constants.h | 8 + radio/src/sdcard.h | 2 + radio/src/simu.cpp | 9 + .../targets/common/arm/stm32/adc_driver.cpp | 3 + .../arm/stm32/bootloader/CMakeLists.txt | 2 +- .../common/arm/stm32/bootloader/boot.cpp | 5 + .../targets/common/arm/stm32/pwr_driver.cpp | 2 +- radio/src/targets/sky9x/CMakeLists.txt | 1 + radio/src/targets/taranis/CMakeLists.txt | 33 +++ .../src/targets/taranis/backlight_driver.cpp | 8 +- .../targets/taranis/bind_button_driver.cpp | 100 +++++++ radio/src/targets/taranis/board.cpp | 2 + radio/src/targets/taranis/board.h | 37 ++- radio/src/targets/taranis/hal.h | 127 ++++++++- radio/src/targets/taranis/keys_driver.cpp | 9 +- radio/src/targets/taranis/lcd_driver_spi.cpp | 2 +- .../targets/taranis/usb_charger_driver.cpp | 4 + radio/src/translations/cz.h.txt | 3 + radio/src/translations/de.h.txt | 3 + radio/src/translations/en.h.txt | 3 + radio/src/translations/es.h.txt | 3 + radio/src/translations/fi.h.txt | 3 + radio/src/translations/fr.h.txt | 3 + radio/src/translations/it.h.txt | 3 + radio/src/translations/nl.h.txt | 3 + radio/src/translations/pl.h.txt | 25 +- radio/src/translations/pt.h.txt | 3 + radio/src/translations/se.h.txt | 3 + radio/util/build-firmware.py | 5 + radio/util/fwoptions.py | 11 + tools/build-radiomaster.py | 12 + tools/commit-tests.sh | 9 + 79 files changed, 1051 insertions(+), 143 deletions(-) create mode 100644 companion/src/images/simulator/T8/bottom.png create mode 100644 companion/src/images/simulator/T8/left-pagedn.png create mode 100644 companion/src/images/simulator/T8/left-pageup.png create mode 100644 companion/src/images/simulator/T8/left-rtn.png create mode 100644 companion/src/images/simulator/T8/left-sys.png create mode 100644 companion/src/images/simulator/T8/left.png create mode 100644 companion/src/images/simulator/T8/right-dn.png create mode 100644 companion/src/images/simulator/T8/right-ent.png create mode 100644 companion/src/images/simulator/T8/right-mdl.png create mode 100644 companion/src/images/simulator/T8/right-up.png create mode 100644 companion/src/images/simulator/T8/right.png create mode 100644 companion/src/images/simulator/T8/top.png create mode 100644 companion/src/simulation/simulateduiwidgetT8.cpp create mode 100644 companion/src/simulation/simulateduiwidgetT8.ui create mode 100644 radio/src/targets/taranis/bind_button_driver.cpp diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index 3d27550e6..3150a86ca 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -345,6 +345,8 @@ elseif(PCB STREQUAL X7 AND PCBREV STREQUAL T12) set(FLAVOUR t12) elseif(PCB STREQUAL X7 AND PCBREV STREQUAL TX12) set(FLAVOUR tx12) +elseif(PCB STREQUAL X7 AND PCBREV STREQUAL T8) + set(FLAVOUR t8) elseif(PCB STREQUAL X9D+ AND PCBREV STREQUAL 2019) set(FLAVOUR x9d+2019) elseif(PCB STREQUAL X10 AND PCBREV STREQUAL EXPRESS) diff --git a/companion/src/companion.qrc b/companion/src/companion.qrc index fbbacdb48..ca17c812c 100644 --- a/companion/src/companion.qrc +++ b/companion/src/companion.qrc @@ -190,6 +190,18 @@ images/simulator/TX12/right-mdl.png images/simulator/TX12/bottom.png images/simulator/TX12/top.png + images/simulator/T8/left.png + images/simulator/T8/left-pageup.png + images/simulator/T8/left-pagedn.png + images/simulator/T8/left-rtn.png + images/simulator/T8/left-sys.png + images/simulator/T8/right.png + images/simulator/T8/right-ent.png + images/simulator/T8/right-mdl.png + images/simulator/T8/right-up.png + images/simulator/T8/right-dn.png + images/simulator/T8/bottom.png + images/simulator/T8/top.png images/simulator/JumperT16/left.png images/simulator/JumperT16/right.png images/simulator/JumperT16/top.png diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 82e645d05..d6c010814 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -87,6 +87,8 @@ uint32_t Boards::getFourCC(Type board) return 0x3878746F; case BOARD_RADIOMASTER_TX12: return 0x4178746F; + case BOARD_RADIOMASTER_T8: + return 0x4378746F; case BOARD_UNKNOWN: break; } @@ -114,6 +116,7 @@ int Boards::getEEpromSize(Board::Type board) case BOARD_TARANIS_X9E: case BOARD_JUMPER_T12: case BOARD_RADIOMASTER_TX12: + case BOARD_RADIOMASTER_T8: return EESIZE_TARANIS; case BOARD_UNKNOWN: return EESIZE_MAX; @@ -149,6 +152,7 @@ int Boards::getFlashSize(Type board) case BOARD_TARANIS_X9E: case BOARD_JUMPER_T12: case BOARD_RADIOMASTER_TX12: + case BOARD_RADIOMASTER_T8: return FSIZE_TARANIS; case BOARD_HORUS_X12S: case BOARD_X10: @@ -232,6 +236,16 @@ SwitchInfo Boards::getSwitchInfo(Board::Type board, int index) if (index < DIM(switches)) return switches[index]; } + else if (IS_RADIOMASTER_T8(board)) { + const Board::SwitchInfo switches[] = { + {SWITCH_TOGGLE, "SA"}, + {SWITCH_3POS, "SB"}, + {SWITCH_3POS, "SC"}, + {SWITCH_TOGGLE, "SD"} + }; + if (index < DIM(switches)) + return switches[index]; + } else if (IS_JUMPER_T12(board)) { const Board::SwitchInfo switches[] = { {SWITCH_3POS, "SA"}, @@ -568,6 +582,8 @@ QString Boards::getBoardName(Board::Type board) return "Radiomaster TX16S"; case BOARD_RADIOMASTER_TX12: return "Radiomaster TX12"; + case BOARD_RADIOMASTER_T8: + return "Radiomaster T8"; default: return tr("Unknown"); } diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 1e1588485..fe00e77dd 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -53,9 +53,10 @@ namespace Board { BOARD_RADIOMASTER_TX16S, BOARD_JUMPER_T18, BOARD_RADIOMASTER_TX12, + BOARD_RADIOMASTER_T8, }; - constexpr int BOARD_TYPE_MAX = BOARD_RADIOMASTER_TX12; + constexpr int BOARD_TYPE_MAX = BOARD_RADIOMASTER_T8; enum PotType { @@ -224,6 +225,11 @@ inline bool IS_RADIOMASTER_TX12(Board::Type board) return board == Board::BOARD_RADIOMASTER_TX12; } +inline bool IS_RADIOMASTER_T8(Board::Type board) +{ + return board == Board::BOARD_RADIOMASTER_T8; +} + inline bool IS_FAMILY_T16(Board::Type board) { return board == Board::BOARD_JUMPER_T16 || board == Board::BOARD_RADIOMASTER_TX16S || board == Board::BOARD_JUMPER_T18; @@ -231,7 +237,7 @@ inline bool IS_FAMILY_T16(Board::Type board) inline bool IS_FAMILY_T12(Board::Type board) { - return board == Board::BOARD_JUMPER_T12 || board == Board::BOARD_RADIOMASTER_TX12; + return board == Board::BOARD_JUMPER_T12 || board == Board::BOARD_RADIOMASTER_TX12 || board == Board::BOARD_RADIOMASTER_T8; } inline bool IS_TARANIS_XLITE(Board::Type board) diff --git a/companion/src/firmwares/opentx/opentxeeprom.h b/companion/src/firmwares/opentx/opentxeeprom.h index aabbe2284..fdb5247a5 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.h +++ b/companion/src/firmwares/opentx/opentxeeprom.h @@ -37,6 +37,7 @@ #define TARANIS_X9LITES_VARIANT 0x0801 #define JUMPER_T12_VARIANT 0x4001 #define RADIOMASTER_TX12_VARIANT 0x4002 +#define RADIOMASTER_T8_VARIANT 0x4004 class OpenTxGeneralData: public TransformedField { public: diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 04f02eb29..f0d50338e 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -68,6 +68,8 @@ const char * OpenTxEepromInterface::getName() return "OpenTX for Radiomaster TX16S"; case BOARD_RADIOMASTER_TX12: return "OpenTX for Radiomaster TX12"; + case BOARD_RADIOMASTER_T8: + return "OpenTX for Radiomaster T8"; case BOARD_TARANIS_X9D: return "OpenTX for FrSky Taranis X9D"; case BOARD_TARANIS_X9DP: @@ -339,6 +341,9 @@ int OpenTxEepromInterface::save(uint8_t * eeprom, const RadioData & radioData, u else if (IS_RADIOMASTER_TX12(board)) { variant |= RADIOMASTER_TX12_VARIANT; } + else if (IS_RADIOMASTER_T8(board)) { + variant |= RADIOMASTER_T8_VARIANT; + } OpenTxGeneralData generator((GeneralSettings &)radioData.generalSettings, board, version, variant); // generator.dump(); QByteArray data; @@ -685,6 +690,8 @@ int OpenTxFirmware::getCapability(::Capability capability) return JUMPER_T12_VARIANT; else if (IS_RADIOMASTER_TX12(board)) return RADIOMASTER_TX12_VARIANT; + else if (IS_RADIOMASTER_T8(board)) + return RADIOMASTER_T8_VARIANT; else return 0; case MavlinkTelemetry: @@ -954,6 +961,11 @@ bool OpenTxEepromInterface::checkVariant(unsigned int version, unsigned int vari variantError = true; } } + else if (IS_RADIOMASTER_T8(board)) { + if (variant != RADIOMASTER_T8_VARIANT) { + variantError = true; + } + } else if (IS_TARANIS(board)) { if (variant != 0) { variantError = true; @@ -1294,6 +1306,17 @@ void registerOpenTxFirmwares() registerOpenTxFirmware(firmware); addOpenTxRfOptions(firmware, FLEX); + /* Radiomaster T8 board */ + firmware = new OpenTxFirmware("opentx-t8", QCoreApplication::translate("Firmware", "Radiomaster T8"), BOARD_RADIOMASTER_T8); + addOpenTxCommonOptions(firmware); + firmware->addOption("noheli", Firmware::tr("Disable HELI menu and cyclic mix support")); + firmware->addOption("nogvars", Firmware::tr("Disable Global variables")); + firmware->addOption("lua", Firmware::tr("Enable Lua custom scripts screen")); + addOpenTxFontOptions(firmware); + registerOpenTxFirmware(firmware); + addOpenTxRfOptions(firmware, NONE); + firmware->addOption("bindkey", Firmware::tr("Allow bind using bind key")); + /* Radiomaster TX16S board */ firmware = new OpenTxFirmware("opentx-tx16s", Firmware::tr("Radiomaster TX16S / SE / Hall / Masterfire"), BOARD_RADIOMASTER_TX16S); addOpenTxFrskyOptions(firmware); diff --git a/companion/src/images/simulator/T8/bottom.png b/companion/src/images/simulator/T8/bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..055c7db62cb0a0eb7c7e030bd50d7b2006ceb716 GIT binary patch literal 16616 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 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/T8/left-pagedn.png b/companion/src/images/simulator/T8/left-pagedn.png new file mode 100644 index 0000000000000000000000000000000000000000..6b290e3688dba87820b9ab96da45889e961595ed GIT binary patch literal 14872 zcmV+zI_JfSP)@54EQ0`UT<5X1;-E@~=ODc^iao>G#iob_pC?EXB2j3ld9_`}$X0tid9`Dn3 z`5k|G55M#J)Bi)?4sox%@=Cn;;*0UiU;Z+F^{ZdSuYdjPg2ZdruFXi*0mW|u2b9MY ziA1U*fJn#n*RNln&u7Rd(s@1maB^}oU-Q2ji9pmba(RxX?(86`z1d`pw#J-gJ07S5|LPZcMh7?3<GQE?>S}kcYqcO{#OHL8(F6nSRd( zoNQH*=#&&9+f?59Gxd6B$>q5h8Htc@k38~7LEbaZJX4VN<~P4NZoc{E@?BXNC=P7( zg%@5ZtQB&RN=L}R;uzT`Sgb1~8KS}{Ni|dF7^(zih;{As!m?8Ukn!a&e>r~m!ygue z-G2M+aplUD84*D6bI&~&Pe1*1Jn_U6h1EhUed(o_3NrD*HOdN=3Y;~n{L}Lx2n<7p zo{x#D^tXajAg{l$iVbAm7ryX?_}R~X7PsDdYrOsKZ!atph=Dc=>U{LkN8*P+e54={ zREl#PAfcOi^kX=8>?;NGQy&@$+h1X3v~ua?eQg!F~Mk$K${L>%R(Bz4M*#EXaU1 z`al2kKSi+tg@Tn*;n0tJN5T+v47horpoqwj5_10%5-)_uMqdh^79bEvy8G_C;v_E_OqXjhaP&Um<>Q4KI*Dc1%!q?Kd!{ISmS_eO?e5?G9xh)eDXwPf`pkH zHAHu>*no9@_q*RM#yhC&^Pm5Gp+HdLEw|iKv{E{+)u;xtVA#_`gX1tO=qpvEjKf(^ zN~lZ%HEqX;?HKqMB4?c%m33xFtg*~UsFBb$67fs;w7MMLhgO>i(ibdeB~Xv`Adr4f3<8h@Dum$=MF$84tHs}#CIX`rOGdj>Xy!(%$wURG zZhfXdyXAvbs_YhtP-6b$KmMa2;;nCeYoRu#Q{d%5nLr>|E@U4R9k5Pnp^P%Py{3?< z_JmAkjut;u@M9TbYA~8#HYQ5P=L9Cw8#KmmC_s_<*MI%jqS(CcZEq{e3S}TD3zP;K z2L!_9cITaU7Njc6^ytKSV2x|-wY2M8Ks8g^yw`hlfP?aYz(*f_w6IGc2QLT&;vDTh zIJIGFyyG43DAy?hl>#GSeXc1+h2m286XfZ zM=E4`1!N#JP*5P^hm?Ia*B2p^bG2(~zh4W~*f;w_rvx}m5fd-I5ZA6)P)EI5CoQqmjoMyTm+ki{6iROw9K@q4APD0eI!!JIw9M^VK71Im?)g(cihjT z02(HYdiq1bR`@db4FH<~a$#({RJgG6)~!Q8KZ{p+*a3-8eT3!%H1q;P*`a!*&rRTJuW-_elKj9dq z3ishX7_=^qiA$G?LUYsQ%W>a(-`fv6WxyBg1Z)yWqNxBm2!uk=<2dtYJU=vp7Arl5 z&92pOkI{rlnRqw67X#eaUVNe0#rHpOfBeB8yssZ5qVqra!4KxN`j|W|^h;7Z1r_4A zCOJjLn6DwYrj=&U(kg3OtJ{2jTz})kWdyIj@=7^>a_KbQ|A7z0d*A!s*qog7gG9K@ z;AZn&P09?g8>S$xb#1-TP(xr;s66YNhqT}*e8-)4#NGGYQ&{Q?FTU81Oav>1c|lf5 zyN^@{Duk?L23W{U&Y}V>kI8wB|62PBOP-mmoz1WdJmW7WDOl++W$l&ouc^1cML@`j zf!{EeM+Y{Pot78nwIU~0DkDqKa%g8zLe1B=yAMnXUzhQk-|DvnFmAcfB@&OnDltAF zEuUoz)U;?_dmi`OQhNs`36>L-&u+4gjBQTmyOD{YLRA|K*V8w; z#-2RG;t_1#o>jUw=6UJ{i8Ln&?%7wTum@5dp;5*g1-f+ZHH_rIQStQ}Z`Ss~k@YKJs(A-k^|N?aRr%M_2=?=`Hg$=+c)^ES=Jf_Amj`XIT$-Yg0x?-m-BVOgAJH3Y_{UUR3W#+Wk`$Z*HJjYqTY_v-#R_)1*HfqS)4y&gF=MC4 zu)vlhy0!MA#@={boewRatv)krCp%ufcJkuTFCmkF%f5@aK2hl(T=bfsC37 zldYvtM}*eD$?hk#oDK)a^!vE`AGFsS5{cRDayc52#dOevM9PW9ANPnFO8_upTVwuWhSmQ$s_XY0AcWaO92IR-Z>5*KBI9P_n= zHEJR|1_F_*gESAMG$MN(6dp1eysga8&y)^M0iheLtUCVHu>GO_nmanre zx|wRnkz>ry-2Lc^60^b6bIg9rRCD*RCd-H9DZmiLUE^Lq@5Wb`BxVIB-giRMfu zW&@-i8h3-$Oq9%R=a9M4!jLtJBI34h+TIv*DV2c~IUb(h5hEr@=hZVJag4hRtC<{r zjIiYT((9HG(z>U`Sz>ZR$Ai_oH%%1Oofp~i55B^)S%D80HkkVQ+NI z*L0)UFz?j&kI?$0HD@+ItSE(La*sUn!?^d}dk-q}*E}j2)COG({2}0sGM)%cz5Cto zo|oNeKWiqh{|AbZkI84<{|K=qGiNl)tU1T|tw0Ah*=Ndb`v+Oq!5 znD}EO8Q>81?R#XPNCFGS<2b@zc&_fd5e)>MfhlQtEFPO}k%_Yh?nX_|$iOQCMG zl%cE(gNSxm3l))lMEJ4xkunm4v>W7I9B(XyPYX?xmSXm-FmkjRV%~2G)Q8X$!d{46 za8a!GJrYU|69WgVcNQ6&sXx0my2^HQ$TZQG;~M8M$Uqnd7_1aEVL*{UFoU(Fitz5+ z4w34Cysh@~i0C6am#C)D?{iJi2T5wu@u$y6+`_)~_V$F}4srn%^ z*#A;4fil2Ck)8oor*%nn=e5ylme%~B;|N)|uPIJ8n;|e{Qe{exUl)S1k?$Nk%I_+9 z_*hq)gW6<745(7Ky;J$B@{Tpc)pR&(&Oj^OBbt;wT7GKleVV>nL!d%e4)E;7m110$ zDD}E5<)T%Usa*5VYK<&8t!=VaIL!!726Z&hCe__fGo{S?RdeSZm_NgziP2S1m}S6f zGq~l<)ZaXPw3ioOeyJRz0Z$4Ab<*TvkzCeT)%MUAkLoXh%kZ=B zxBEWO#OSH;f+8_0k@%L|jmNa+DQ!ctzM_Oo_u<%0uU?B+uk9!g=e3$6?$b(#A$x7$ zqxE#OKiLG!%%+;*k9-@N7QCb@J5gP4OR^+GCMgQ%N{zH@*A8~4;aZPfBGM)@SsdZ9IEdWHG>`SYV{dy^Cu7aXXNV!Y7*jmk{{cH6- z*ImB@z+m8$EgbnPV10JYw;3|PyylJCA$(wzpKXsF<=FV(>v6ZK-1+U$^0kI3LTmb@{qXuW z*|q0|xzF=T#m<7l1PvFtBBOG#hM>%B7$w%I$Za8VErM$-zV&`tI|Qck4~#Aj+rCm3 z9XXjtBZExNMWP%zoR@medtYWUH60w&W4g=$Z^^Hv8J{`MTWz-3a_zatQlCHHxricd z>fw|F)LAHMS?#W~&X(Dc-4jQ!%Qe@o0iRgj_k9E-ih&<2#?TWI8RGHqj#H1zGC!{O zI<%0nMkB60%g-%6w>9aC+&9g)D?3qhT{gB0cT3Z3K?^xfZO2gO8ptNq8ga0{r!#iS zSYRfjMhQtBYhdf|EQ;V%Ra3C^`s}_MwAix0!L5_Y~pK|Suv?}@|Fm^4vg zhw$+t!~Cwh?wX4a%_)-fdoFMZ{X3rvE*qFu4AZx}dK7OEXvv|P{in!gQ~_nWuz?st zNb0~Ir^Mf^NN(6jp|_fU}>ZR z)Q7@(7+9=|!k&wRgtMYDS-d~m7o}cI0#G@sEaEx%j#_e>ayiH{J?*EzZ{1YcAL|N< zS*{9SG*cq@n*)sdQKSzP2*kbr{qHaAt`%%{ZC4c`pGZ8<1S}M-ef;sq=S8N!|NFmx zhQi|rcAb_N=qXW(F+k5dRjA2LQXvKw3qLWS*aZ{|X=t`Mn`}7;9BKYM+O*7^17%`U zYb@Tuq0Q9Bkk z5%2*uV!a}$-xV9b&uj5y$QxmrGc!Fx2C6oIuLZIRk@cm4koB3 zo%U81%(c@RLc<2f%cC5FxZ1$h*z{;_KK*1{0v-#ydzhH9gRYJJ5H`zms;bu<0wEq3 zy1+8o+M?*J&Fs32WiGW*JwKIP*|J1SZ}CK31R9BLSd%ddte2YEYfeFW+^_R%vfp}~ z3v9TUHIv;f&3E6}PAS^&kg{m8}_MS1q@yAvP2CTWPNax z2OEmPQv`!wO`6s-7N0-vz2IG$7|QlIKv_n(4gjMDpacve{FiXQp(I#L30PioER$*e zyLCLK^&9ZkAmEHTZ+JdjaHCZquDxH)PCP&^pY_^?^|uQ;yWRVe4X|EA;~vHMP`(l& zBK*Glo_ppjx|-JOpc5s#jueg%)I$$FRBFc|8QO8b5&p;(^@^ zSZLwyyYG%`*RRLJk315;{ME1Gt~>51)j1AU^iJLtrTnm^0%5X@$G|?3E%3w>PZXy& zo`=oFLN--dg3NzAPqg`XXOaDk%KW^_Nj0_!Tu%=+cmMm|7f(I?bX>jqbUgpubGvo? zr?QrC8mxIBr)FquZ-$Lrx8J{l#g6a+`~JWOJ}_^)M}?}UF^92og)`ZF(Ybb7^MJJM zKq8h2OK9(1?|f(6cI&N$y+ZLQ3q`k+gL*T0LJFR?^f*uDGsSB%J?i@Q`pyIxh7vOb zJnPRN~N1#*jl4&zcCBeo~w9^|sDC}&DaX421G8Rkl>jv4d8SUM2^DS}9%{T9q zA1+HfLxtOj1J)LEVfEdk_A7YF+F8w*#TjaQVyxAIdEIuh7ejKTZfGRB*M)_IwEQx` zcOh-HHEVh?RVywNgVIyqX2L&24?Xem(JHeyDfTZa9lY9 zweDVu)+#$?<)>P;#mvrAP%RTw0`>nOMFPXMjZ%Dw#f7e;9?euxC?0M8E+8bsR7;Cex9qB1%7ozs|?(Sfn=;D{oqtWgSG(;lgq+qP@9 z{aUFn&qi13WW7`^V5?D=*YNgx*WctF;s|sXPmG3FCL7e2fjvqEs9{R-<-$`UK<}xs zSO>CIP1pB()|waDb!CU|aj&3tM~T@0y0=ERO2}+87W?xAD(-bbg=Q=J_7lk&n^hUA z6M{04=UbC6mJmDpOC{rN?9A=v(DIS$Y04UySo;FAUy4JGC&$#_A@|$jVB{H|YB}(lxyAO}M2C4&ITk7U= zOf}RHB(*$2ONik;8nAXb>h0_sJbQ@B%)siy(OFrrm=Um<-_Vr|U!PcGp?ZF>)DD-4 z-kuJ=nlkxN-@}O z0j@dMkn1cP6PQ_c+leZp=S?sjahl)a1Mk$8A5;#LQIR}#ZV`?B9cfPWLbf-St3&9 z@@hoNJMGpd?vl2~-nQOTs&EakRhP}>6WT$Z3?loFJn~4X!Nz@_V#;F6s5gYgk$v(U z5lW2}?IQymDg5(ReiOC28RYMi{oWmng9Ir$ZMyT=xTaTAPCUnj=YSwMrjfe;?Qeg( zBt{|ao+*W%@at?kOcryn6;UkEhm}{4J@#1KfB*gQwzs`)t~o&287BAIFd=RhDe$RY zKnJ*LjH(gxUalDg;=ca%ub06_(L9vu($vMwM5sa57qV462P>n#_r34M``-7yxZ{pH z&XfeJnM7yWB?0Q>?a|n;!Dvy749G??xTv!Rgn>e^@Dt107{qlOk%-k~D%DU`4(o5` zuY#q5B2n3qCBnvOTwQ@oO_fXMbT3B8M!AnOibjU0dB`fEt|j{pKuwkFK(YexE!W|EE&iQ& z@rllmm=#)B0yjG-_jDu7Fsjaq7xCF-wQ6r>T5w1ThZeAP&r(f^N+;2qQG>G1!JN~2-c1w-cPwP`xb}O^@hrYs>x)C#W1rrd>0Fm7i$uaGnQb-A zQhyKPW36elnRh2DNYn2vBx_$4S`#VbIJ^GZ^*t@9;XrUXS!Uq7ZRq!UNwGr*b6w76 zlV`0H+uMi*IqH2T988Lk3D}!<8pcv1i52pk`Yj0=!c=AsNrb`|&O|c6VF=EyaRa{P zu*mv*fW+-z z)?yFc1%=m`85-Nm?u9|_6f--ox5K;+(CxmAmGVAkwUJDgwd}X`?z=UK(pGP2{83TK z2g{R*M8Zn7MiGJQQHy4{j>-%BXaghnK5>X(P8g*=_26)=Bc2zrFY3^Gr`B;& zAFB}!y%y-pUd|DztdN6>^Ki3aK(Uw8h>&aaKz)DoM}JhxSbphCUn()bKm5Z#ERJeWP>E!eQYM$<^wQ~+3b8q0*<`cG4q*In zoEE8KKPrTt&_1dyB9@4#30Dy_BVY+!2JewL{cqSUZ|gEBtl;)yjV1A3#_@p2KmOxC zF1xCM)qVD}pFOBEzTrk9lUkp}74=!%a}mc`I0RD>}aC0sEtQ{P^CJ(oKlwq@cYjtZP@x6kDGRCL(WO+844CU-v2Q~!3ny#3=OJ?_` zj*0!rJ(uV?J)`ERc7r=)pm<)rI2>SAYCIquHmFV|S*oNeJsI-+9Wq#Cu|3MfEQI3} z;D6mvcgJ~1V$&ee5LKEocAY(6jeUQf=A0^I0b)XV_EffWdf1K&Y^KRLQDm1jz%xo) zb6to`^nk9b`fiMKB6wQAYMZ_`F$~G>^Yy;^vtm+a_DLB-sVIf*`}Fz?k?j^qG(%sK z=77q=LH5J>XSeF0o}HYO!`rz?UsrbGAnPJ( zvceS@{f$8#q>KrvjpZ+Sd^?rIWZBEXD*aoF&-^kkSzpmdja$}%J^_nP}2bug&*(_w)9@L3LwsfxPBg6=qVf&+QH9*Ach z0U(WK#n=bt*674x-#CHL9?mldM-e#Ew3(_b79?U~oSepR(y6gemX)PBWJ|`>q##?P zQ#&&W$R=@&K29n;gKWJ%Ov?dCL8QPiMP`&@wj0~wbTgF?+fH@h@LX0ro<+mx+BOR4 z4%3zmuk@?f5R_%AHmM4TtNxhzia!nw5G1*W#o6s3$-a6o|B~{^UdhT>XH(j|B&;Ih4@s%%sxxaWK7o%jX(>))VVLh6!lgK5afE3Cj zQWMPzWrJjupi-X%L@GYefWdA~njiYKl z56%~v{)UY98wBp!s(Jni>X{jAV`+DLaVeU5P*j$N_-=Jwh7L5T5ChF!Q&kSCtmbpn zz1MQTQTgG7Bjp!o!HtYGhari(MI}i}2ZPj<&GxWb0Pg2hB!x{3-$ibexS{TiF(xrX zcT!{%)BB2vhW=rRT(K2j-MRLMRK*HsOH-llte6uFK_Z+A2&w<2U)*|n`k zdES?pCL8R+C|mt)V!sz$Z zl%Q-tGiu9NUp6sXh;Ox*fwJ}xjkr4`Qsc`M10Ap$rG-$TBP>&B99o{CSfKElHeHkP zHBjGdm)hyl@7f?cMPv#Dp|-B0H7WF&VH3qC$=bG;*f-C2Z72G z5;?%`;cv0gjKb78fG7f1NMYRsy(cZoSyFsLyf?7vko^ts4gIZa<+i_D>$>iehkXqO zK}`=0+Ga0L1Y%LmPk;K;5<;pO=9#k7GDEbDt5>g<57uD-nYxZd-zn3IYvz#4*yyv6 zd!0Rcey9QmizW)v_8)!p(LxvD{l{rpD;>+Cd!G2oF6zMa3U@;DNFl%A*`QU>TLT#;MmcGXSwjuxd~{ zR!Kn&C`Cp^#zulb2R+yAu5HJ_D8(5tcvxy+j4JVR5Ek0_1{ zWTHSH!&yrP{u=WmdPAZ{BVwhOgFr-r#X$yQ!6zv&%l3{H#(2xCL&^stL4BAk&p!KX zp-lYN=Vlb%5()myt`en~HJz#2gKVpissDUo#^azdW)(4Xg7`6Kt=b1oe^mEhO!=;$ zKukiga!rF&sF^H&jZdoXBKxGo>?IXpwVBL&Ys<#9E&rY=JvA65`p)%=uGG{t(N2FHHx;@qMwAW6 zXlnsmZG^4SSc0{zjo@;R&#NsqUE6MUt~(^E%dMt-WCl8+RqWuHeq`SXHCNwY8xN&t9wJsM14_j}B2xa;i$hqe z_Ag!UtezR;zP>V6Og?{)GviY2-3 zAWRdcbAr!<8%`66Gczcg6#iZZT6s@MbbCvW#|r8JH6YCoZZG&xQKSzZ9Im}!B9@mx z;fKe{b(@nj*XwtBm+(QEFq9sn3y#^}Ey`=jZAW;wp(uR`zPuN6&um2uF7|8!5+8o} z;c^_+59v`EY$NCF`I)cNWh~kBlTSWbcBg^64G|Nr4)#I}YmilYuWs{BTLYXK`!T&d zsSySFFu2%^3yx_Zk~P>IA=?3Qb*wm016%#{Wno100D-elbFsS9h2Om7xBMSdYjfzsJFXnP1^8}fjoRD3L1MQ>} zD{vq%jP~!l@4mS6&N~ZrW(ce~6PA$7cDh7jD~yvVJ5H5c5e!74Kp)sEBK=T4aLyNo z`k>eBz;Vh2#b2yDy#4mu%U`Y}hWypG)@avDj)*SvPN_^}Gg`IDT9jgtv_~Keh{Wb! zSl>VdWQ%cTwrMbHqQj1A)sL&o*SxDG9RYS$Gd>ezw&T>^u``eLmY^#RN7qnV*w z?73WZhqcPfO#Y5ilpzvI%lfkhkq%v^@ek9wz%kjHUG2uqh|grqC@YAyB4TaO^|)5M z$o#mvULs3)&Jb5K?rVLb&D@=x=tv~gW^(rO-lkrAj5{nd_H`mq{#c1VGk zc+h^kqr`dkB|%m~Mj4~5&DZlZ%6&PuS7Y?&koqa_LfAc7zOS(lq0(Vmp2oJFva@&R zdJbykbwK;x55 zG8S{t;(sibLd*Oh-CZ3!C(3IhX#jaCjC z&(i9_?gHRPxLpIZkCM$;zBk)XPHkpDdJ!QMSutTJue- z4-b;cuZm5GbAh#Ka1AC6DIV=S_Sj>E^#ghMu(Fk#EZrfI)a1*<6lscFWqZ6%ZAHCC zi|lbXH`7E7V;yNv9>CyQ~NSVO><@}fGY7kP$t(+LV z4T?gyn_y07l_JKCY8bibcC=V2f zX90nTacW9R_U;#Ik?A10G#MLZ^gb)mMTBW3Vg(%M7Pt6NlceIKtdmHlsZi^WdSC$0 zJo8Mk0r4zs4@eVYDL1xzkB$Du0D=X9eUa^{4TzcFayn~;VAm$K5E21MQcsV1ib5(K zXH-u_5=q$U6iN&@<_DYOmG$Q8Q;0 z+%!?2EfEO&4~9JYi~u#4-8l=5io(`qP*{1&{ zynx~gKY4sM(p{eUz|`zbmt*=|XUjUUE(fW=-~+i03dCQOM}Zf{DNt?A76PN^vS2Q2 z2td~Dx7}X0Sikzr)uKHj8LDi}er;#BVJp`%2E*}b`>np!C`a>cknf5(AijeFxqjc- zvwzQE>@nLdo0Wsb5b^u+<(rCp0SkrJ2jdwkJ7nag(@Q1ksc4bLDl=+y2;2hH2MttXiM5HD9akiGxgBj(@?X?myK?1nQPsh|;6O&AFsoSHcI&Ni31Oy_ zP01DASaJ9oE318XiWy{!1(qFy{}avu_CY;DnfJ(_7YWPmQn85yk=a^5UIY0FmW7!Q zii8%34+aqaO2z{>vgJ?4#xjI$Y%2seUl`e-6CKYZH_(8uzms!@3>=x=#uIC7ZLsZQ zF<=-t$V6y>kV&MBtrdM=l0p8gND!b#Jh%FTq6Mv)_FsrXmbK~a>anbE-LA!miXYVx zL9G?u6WC&KCexwKO|`O`aG7T(lbzW;jFf=l)j__r6nzb3_jg@*Av;dc^Sb9RccI}< zGTv53&DrxZE1fGyCBFmBoo_&(9z;O)@RCM)lq| zw{oO)Ln5=Fg$lI(P%kJ&Y*pI|(Q&VpEx}*6=d9Gzj z)CMc0&FpyXx?1~w?RgcRF1Mqt)qz=><*_2Ld<-m^1lhA{@^48Vw%)5tB(_Wohoyn8 zL2Ce8TIp+T`#kq`heX|!(sNESH(AT{$j+^0Yc*NbLgY{>(V6u*^Z< zpU~1|YlCtu9D7fzPv?Q|6dNWeG40TyCYzS7eXWS7DKs@3bf{znta+y2*BvFo2Z@zH zT(z`@J^D31$V>8V?I0_n9C=xvYGNRJmkhdPrwHYsqzL!L57SuPAU@VzmT8Y`sr2>K zd?T4;wF%K35)sltVIM9Di%lX*eb!diB&{cyIsmZi4()g2Pyxc<*@yKoJA=eu=@M6lAo z{oB7Sk#iPK2r2g!uLFf0qn)1D6LhQ`tWCadqZohkCw~$j{NM)S?u}(doEdy`0 zmD#*qveQ{3+ic=5{^BpJLh$xJ0iPfrvPNJfg14_ z8EizJ7D4b5&}Uib?)!OdMZ%H`Z;cYvGBsMy?HY*~fWQ39zl^{7tG_CFrI3#_{Gsr8 zc}lkncLS80BaZm;nf@uPwKZ$5wWSF&H0qwueH~vfUh-Rlj9P%ezy9mLF6-QNBW9!v z;=7mFBD5%PKF0w|&L6AX)P}m{2&nZ>ukU(u6TSrUNk8$4Pn2wL?BUD8KVYe>X3;9} zUVBctc0swxpyg>f544n}u4SUX)Q3O(;rRH+KVBjnD9lbphxK7t2~p)>7&2<{ax0auT74wv)!B;KK8MX#XtPRKNOaVo~Tf8yQmk zTy|~ko?83Jw*R}o`@6EF4TuDF?y6%%oSy7zs-0d!s>f!g#E^7Gf)8S*?Zyfj z8AhGXdb4M^rkp+V!Xl9XZT0W}{_o>+pZi>qk+nKKNKe? zr*X?IZ;rRZ6e+buckAd+Q5iG;{I8Yq>*GW za@oQ~M`BFu6}BX_*H3=(lSNy_n!&f+dV5^CvRgB_If=qrPfvE)bf;xguMIJ{ul^r9 zwtMIcjWb(joIO8GNE8Il&d{Mx0JX5yL%JhFGXD#mYd|7d6vJ#Z>~$c_uhNsBY*m*@#N2bR#ptZ{PK%&db%rT zi4P0(1*u>d4$$E9o|MtH35PS+?62Fi)J&P-Gj`_DSsS%mw_6Cydj9$6<5QpdRDAyP zpD!{Hh=grOj}89f!pvPuPO zkK#ila-Rw=$1a79wivYSEbCr*Wmi!@ra`CdQdMP`kU)9H!ObEeF2A@N9AUGW3j0mk z>M-s6rW4}e=P*mcasVv0`z;kP;6NfOG{JAGo*70lw3<`M#wEM+yICF*q^+fR9GDa_ znAAIvbXBO?h6n3P(zXs+FPrEVi3&g(N=zc22)0U!)5Ix91b<@yw%hAxBJ3HDZ!F{1?JU*(#)XsO0 z#FvY5^4u){BFUj~q^7ffMRuFJ<9u*igYD0&K`W|KgYJXzir64XLSYvO^5$c#$8O}{sK-rbw9l^sKC*Z83L&Z1uL+vRE2-Z6a)W1|Wg zvX6~Tc29|X5#9U3DJ!c#nkh0ZB!(f>Cq~Ww%QWCj9>xmfWgKMeajz&J6vhK}%LBh= zjR~RcFCp05&JdGT^Y`l%+#jm4a*0B_-xou7_|zJ1UWhgt6r|s?0b8r;FDWOPOshE- zT;ZuHYh6;J19yZ`Bu2tQ2^l4Yo48Ol73tMK%k`~?K7=FBaBm!|jqSm(w*+45SQ0z)iW>+q4vZ?nhvB;WH z-%@CFviP&J{#*z<5wSrAbGfKSm7J`$DIuezngO0sVhu9IBtuF~`O5lR`+Xry42{UE zS+OMlT;cKKz6VE3-o*+QS?hnTfBw5B=P!gzPhW=6S7aLjdC zpIiOy7Kua#H=36tlpCV0000< KMNUMnLSTZhF8A~R literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/T8/left-pageup.png b/companion/src/images/simulator/T8/left-pageup.png new file mode 100644 index 0000000000000000000000000000000000000000..c155649a1a55a508822ffc212c29d86f0b550258 GIT binary patch literal 14853 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 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/T8/left-rtn.png b/companion/src/images/simulator/T8/left-rtn.png new file mode 100644 index 0000000000000000000000000000000000000000..95cc700f64757fd7fa98a195b23efbd7c9ab38b0 GIT binary patch literal 14883 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 Vy*jtNAOZjY002ovPDHLkV1jd<3vd7c literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/T8/left-sys.png b/companion/src/images/simulator/T8/left-sys.png new file mode 100644 index 0000000000000000000000000000000000000000..7943ed93d40cc6a55e6321a21861e1049c43eae3 GIT binary patch literal 14832 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 W!^Y(+vx)Qo0000i2M&yIs1b&t5fX;@ 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

Hj9b2td-REB&)<6F7kGp}v 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 O0000Z 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 e$!3ud5&s|S0@M!;dp`C60000~ literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/T8/right.png b/companion/src/images/simulator/T8/right.png new file mode 100644 index 0000000000000000000000000000000000000000..1016d8de50338748ea2e24bd02d738dc037e3d74 GIT binary patch literal 15355 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 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/T8/top.png b/companion/src/images/simulator/T8/top.png new file mode 100644 index 0000000000000000000000000000000000000000..962119223e77db691f915ee748df48155614091d GIT binary patch literal 3878 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 literal 0 HcmV?d00001 diff --git a/companion/src/simulation/CMakeLists.txt b/companion/src/simulation/CMakeLists.txt index 9b9cbc130..c2707a5ca 100644 --- a/companion/src/simulation/CMakeLists.txt +++ b/companion/src/simulation/CMakeLists.txt @@ -16,6 +16,7 @@ set(simulation_SRCS simulateduiwidgetJumperT16.cpp simulateduiwidgetJumperT18.cpp simulateduiwidgetTX12.cpp + simulateduiwidgetT8.cpp simulateduiwidgetTX16S.cpp simulatorinterface.cpp simulatormainwindow.cpp @@ -43,6 +44,7 @@ set(simulation_UIS simulateduiwidgetJumperT16.ui simulateduiwidgetJumperT18.ui simulateduiwidgetTX12.ui + simulateduiwidgetT8.ui simulateduiwidgetTX16S.ui simulatormainwindow.ui simulatorstartupdialog.ui diff --git a/companion/src/simulation/simulateduiwidget.h b/companion/src/simulation/simulateduiwidget.h index 66939f429..af965684f 100644 --- a/companion/src/simulation/simulateduiwidget.h +++ b/companion/src/simulation/simulateduiwidget.h @@ -116,6 +116,7 @@ namespace Ui { class SimulatedUIWidgetJumperT18; class SimulatedUIWidgetTX16S; class SimulatedUIWidgetTX12; + class SimulatedUIWidgetT8; } class SimulatedUIWidget9X: public SimulatedUIWidget @@ -278,6 +279,17 @@ class SimulatedUIWidgetTX12: public SimulatedUIWidget Ui::SimulatedUIWidgetTX12 * ui; }; +class SimulatedUIWidgetT8: public SimulatedUIWidget +{ +Q_OBJECT + +public: + explicit SimulatedUIWidgetT8(SimulatorInterface * simulator, QWidget * parent = nullptr); + virtual ~SimulatedUIWidgetT8(); + +private: + Ui::SimulatedUIWidgetT8 * ui; +}; class SimulatedUIWidgetTX16S: public SimulatedUIWidget { diff --git a/companion/src/simulation/simulateduiwidgetT8.cpp b/companion/src/simulation/simulateduiwidgetT8.cpp new file mode 100644 index 000000000..ed86889d7 --- /dev/null +++ b/companion/src/simulation/simulateduiwidgetT8.cpp @@ -0,0 +1,67 @@ +#include "simulateduiwidget.h" +#include "ui_simulateduiwidgetT8.h" + +// NOTE: RadioUiAction(NUMBER,...): NUMBER relates to enum EnumKeys in the specific board.h + +SimulatedUIWidgetT8::SimulatedUIWidgetT8(SimulatorInterface *simulator, QWidget * parent): + SimulatedUIWidget(simulator, parent), + ui(new Ui::SimulatedUIWidgetT8) +{ + RadioUiAction * act; + + ui->setupUi(this); + + // add actions in order of appearance on the help menu + + act = new RadioUiAction(5, QList() << Qt::Key_Up, SIMU_STR_HLP_KEY_UP, SIMU_STR_HLP_ACT_MDL); + addRadioWidget(ui->rightbuttons->addArea(QRect(25, 40, 35, 20), "T8/right-mdl.png", act)); + + act = new RadioUiAction(6, QList() << Qt::Key_Enter << Qt::Key_Return, SIMU_STR_HLP_KEYS_GO_UP, SIMU_STR_HLP_ACT_UP); + addRadioWidget(ui->rightbuttons->addArea(QRect(25, 90, 35, 20), "T8/right-up.png", act)); + + act = new RadioUiAction(7, QList() << Qt::Key_Enter << Qt::Key_Return, SIMU_STR_HLP_KEYS_GO_DN, SIMU_STR_HLP_ACT_DN); + addRadioWidget(ui->rightbuttons->addArea(QRect(25, 135, 35, 20), "T8/right-dn.png", act)); + + act = new RadioUiAction(1, QList() << Qt::Key_Enter << Qt::Key_Return, SIMU_STR_HLP_KEYS_ACTIVATE, SIMU_STR_HLP_ACT_ENT); + addRadioWidget(ui->rightbuttons->addArea(QRect(25, 180, 35, 20), "T8/right-ent.png", act)); + + act = new RadioUiAction(4, QList() << Qt::Key_Left, SIMU_STR_HLP_KEY_LFT, SIMU_STR_HLP_ACT_SYS); + addRadioWidget(ui->leftbuttons->addArea(QRect(60, 45, 35, 20), "T8/left-sys.png", act)); + + act = new RadioUiAction(0, QList() << Qt::Key_Down << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, SIMU_STR_HLP_KEYS_EXIT, SIMU_STR_HLP_ACT_EXIT); + addRadioWidget(ui->leftbuttons->addArea(QRect(60, 85, 35, 20), "T8/left-rtn.png", act)); + + act = new RadioUiAction(3, QList() << Qt::Key_PageDown, SIMU_STR_HLP_KEY_PGDN, SIMU_STR_HLP_ACT_PGDN); + addRadioWidget(ui->leftbuttons->addArea(QRect(60, 135, 35, 20), "T8/left-pagedn.png", act)); + + act = new RadioUiAction(2, QList() << Qt::Key_PageUp, SIMU_STR_HLP_KEY_PGUP, SIMU_STR_HLP_ACT_PGUP); + addRadioWidget(ui->leftbuttons->addArea(QRect(60, 180, 35, 20), "T8/left-pageup.png", act)); + + + m_backlightColors << QColor(215, 243, 255); // X7 Blue + m_backlightColors << QColor(166,247,159); + m_backlightColors << QColor(247,159,166); + m_backlightColors << QColor(255,195,151); + m_backlightColors << QColor(247,242,159); + + setLcd(ui->lcd); + + QString css = "#radioUiWidget {" + "background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1," + "stop:0 rgba(255, 255, 255, 255)," + "stop:0.757062 rgba(241, 238, 238, 255)," + "stop:1 rgba(247, 245, 245, 255));" + "}"; + + QTimer * tim = new QTimer(this); + tim->setSingleShot(true); + connect(tim, &QTimer::timeout, [this, css]() { + emit customStyleRequest(css); + }); + tim->start(100); +} + +SimulatedUIWidgetT8::~SimulatedUIWidgetT8() +{ + delete ui; +} diff --git a/companion/src/simulation/simulateduiwidgetT8.ui b/companion/src/simulation/simulateduiwidgetT8.ui new file mode 100644 index 000000000..6370888ea --- /dev/null +++ b/companion/src/simulation/simulateduiwidgetT8.ui @@ -0,0 +1,258 @@ + + + SimulatedUIWidgetT8 + + + + 0 + 0 + 480 + 300 + + + + + 0 + 0 + + + + + 480 + 300 + + + + + 480 + 300 + + + + Radiomaster T8 Simulator + + + background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 255), stop:0.757062 rgba(241, 238, 238, 255), stop:1 rgba(247, 245, 245, 255)); + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 113 + 280 + + + + + 113 + 280 + + + + background:url(:/images/simulator/T8/left.png); + + + + + + + + 0 + 0 + + + + + 254 + 158 + + + + + 254 + 158 + + + + background-color: rgb(215, 243, 255); + + + + + + + + 0 + 0 + + + + + 113 + 280 + + + + + 113 + 280 + + + + background:url(:/images/simulator/T8/right.png) + + + + + + + + 0 + 0 + + + + + 254 + 35 + + + + + 254 + 35 + + + + background:url(:/images/simulator/T8/top.png) + + + + + + + + 0 + 0 + + + + + 254 + 113 + + + + + 254 + 113 + + + + background:url(:/images/simulator/T8/bottom.png) + + + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + 16777215 + 12 + + + + + 0 + 12 + + + + background-color: rgb(255, 255, 255); + + + + + + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + 16777215 + 10 + + + + background-color: rgb(247, 245, 245); + + + + + + + + + + + LcdWidget + QWidget +

lcdwidget.h
+ 1 + + + ButtonsWidget + QWidget +
buttonswidget.h
+ 1 +
+ + + + diff --git a/companion/src/simulation/simulatorwidget.cpp b/companion/src/simulation/simulatorwidget.cpp index cab7600d4..0b2b6b853 100644 --- a/companion/src/simulation/simulatorwidget.cpp +++ b/companion/src/simulation/simulatorwidget.cpp @@ -100,6 +100,9 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface * simulato case Board::BOARD_RADIOMASTER_TX12: radioUiWidget = new SimulatedUIWidgetTX12(simulator, this); break; + case Board::BOARD_RADIOMASTER_T8: + radioUiWidget = new SimulatedUIWidgetT8(simulator, this); + break; case Board::BOARD_RADIOMASTER_TX16S: radioUiWidget = new SimulatedUIWidgetTX16S(simulator, this); break; diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index 47a1fed2a..b3a8c1fdb 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -345,6 +345,10 @@ if(RADIOMASTER_RELEASE) add_definitions(-DRADIOMASTER_RELEASE) endif() +if(RADIOMASTER_RTF_RELEASE) + add_definitions(-DRADIOMASTER_RTF_RELEASE) +endif() + if(TBS_RELEASE) add_definitions(-DTBS_RELEASE) endif() diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index dcdd6e5c8..005fbec56 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -208,7 +208,11 @@ enum TrainerMode { }; #endif -#if defined(RADIO_T16) && !defined(INTERNAL_MODULE_MULTI) +#define TRAINER_MODE_MIN() TRAINER_MODE_MASTER_TRAINER_JACK + +#if !defined(HARDWARE_EXTERNAL_MODULE) + #define TRAINER_MODE_MAX() TRAINER_MODE_SLAVE +#elif defined(RADIO_T16) && !defined(INTERNAL_MODULE_MULTI) #if defined(BLUETOOTH) #define TRAINER_MODE_MAX() TRAINER_MODE_SLAVE_BLUETOOTH #else diff --git a/radio/src/gui/128x64/lcd.h b/radio/src/gui/128x64/lcd.h index c6b32dd3d..3a94f2365 100644 --- a/radio/src/gui/128x64/lcd.h +++ b/radio/src/gui/128x64/lcd.h @@ -181,8 +181,9 @@ uint8_t * lcdLoadBitmap(uint8_t * dest, const char * filename, uint8_t width, ui #if defined(BOOT) #define BLINK_ON_PHASE (0) #else - #define BLINK_ON_PHASE (g_blinkTmr10ms & (1<<6)) #define SLOW_BLINK_ON_PHASE (g_blinkTmr10ms & (1<<7)) + #define BLINK_ON_PHASE (g_blinkTmr10ms & (1<<6)) + #define FAST_BLINK_ON_PHASE (g_blinkTmr10ms & (1<<4)) #endif inline display_t getPixel(uint8_t x, uint8_t y) diff --git a/radio/src/gui/128x64/model_curve_edit.cpp b/radio/src/gui/128x64/model_curve_edit.cpp index 66f153b4d..c2168ed02 100644 --- a/radio/src/gui/128x64/model_curve_edit.cpp +++ b/radio/src/gui/128x64/model_curve_edit.cpp @@ -165,7 +165,7 @@ void menuModelCurveOne(event_t event) } break; -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_MDL) case EVT_KEY_FIRST(KEY_MODEL): pushMenu(menuChannelsView); killEvents(event); diff --git a/radio/src/gui/128x64/model_input_edit.cpp b/radio/src/gui/128x64/model_input_edit.cpp index 840e41a82..ce76e0a89 100644 --- a/radio/src/gui/128x64/model_input_edit.cpp +++ b/radio/src/gui/128x64/model_input_edit.cpp @@ -49,7 +49,7 @@ enum ExposFields { void menuModelExpoOne(event_t event) { -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_MDL) if (event == EVT_KEY_FIRST(KEY_MODEL)) { pushMenu(menuChannelsView); killEvents(event); diff --git a/radio/src/gui/128x64/model_mix_edit.cpp b/radio/src/gui/128x64/model_mix_edit.cpp index 92e10ee69..281256f46 100644 --- a/radio/src/gui/128x64/model_mix_edit.cpp +++ b/radio/src/gui/128x64/model_mix_edit.cpp @@ -85,7 +85,7 @@ void drawOffsetBar(uint8_t x, uint8_t y, MixData * md) void menuModelMixOne(event_t event) { -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_MDL) if (event == EVT_KEY_FIRST(KEY_MODEL)) { pushMenu(menuChannelsView); killEvents(event); diff --git a/radio/src/gui/128x64/model_select.cpp b/radio/src/gui/128x64/model_select.cpp index cf6410d63..bcf04117d 100644 --- a/radio/src/gui/128x64/model_select.cpp +++ b/radio/src/gui/128x64/model_select.cpp @@ -203,7 +203,7 @@ void menuModelSelect(event_t event) } break; -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEDN) case EVT_KEY_FIRST(KEY_PAGEUP): chainMenu(menuTabModel[DIM(menuTabModel)-1]); killEvents(event); @@ -212,7 +212,7 @@ void menuModelSelect(event_t event) case EVT_KEY_FIRST(KEY_PAGEDN): chainMenu(menuModelSetup); break; -#elif defined(NAVIGATION_X7) +#elif defined(KEYS_GPIO_REG_PAGE) case EVT_KEY_LONG(KEY_PAGE): chainMenu(menuTabModel[DIM(menuTabModel)-1]); killEvents(event); @@ -244,7 +244,7 @@ void menuModelSelect(event_t event) #endif #endif -#if defined(NAVIGATION_X7) +#if defined(ROTARY_ENCODER) && defined(NAVIGATION_X7) case EVT_ROTARY_LEFT: case EVT_ROTARY_RIGHT: #endif diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 004737515..468597b29 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -111,7 +111,7 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_2, ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_3, #endif - +#if defined(HARDWARE_EXTERNAL_MODULE) ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL, ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE, #if defined(MULTIMODULE) @@ -156,6 +156,7 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_EXTRA_MODULE_CHANNELS, ITEM_MODEL_SETUP_EXTRA_MODULE_BIND, #endif +#endif #if defined(PCBTARANIS) ITEM_MODEL_SETUP_TRAINER_LABEL, @@ -183,13 +184,16 @@ enum MenuModelSetupItems { #define REGISTRATION_ID_ROWS #endif -#if defined(HARDWARE_INTERNAL_MODULE) +#if defined(HARDWARE_INTERNAL_MODULE) && defined(HARDWARE_EXTERNAL_MODULE) #define CURRENT_MODULE_EDITED(k) (k >= ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE) #define CURRENT_RECEIVER_EDITED(k) (k - (k >= ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL ? ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1 : ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1)) #elif defined(PCBSKY9X) - #define CURRENT_MODULE_EDITED(k) (k >= ITEM_MODEL_SETUP_EXTRA_MODULE_LABEL ? EXTRA_MODULE : EXTERNAL_MODULE) + #define CURRENT_MODULE_EDITED(k) (k >= ITEM_MODEL_SETUP_EXTRA_MODULE_LABEL ? EXTRA_MODULE : EXTERNAL_MODULE) +#elif defined(HARDWARE_INTERNAL_MODULE) + #define CURRENT_MODULE_EDITED(k) (INTERNAL_MODULE) + #define CURRENT_RECEIVER_EDITED(k) (k - ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1) #else - #define CURRENT_MODULE_EDITED(k) (EXTERNAL_MODULE) + #define CURRENT_MODULE_EDITED(k) (EXTERNAL_MODULE) #define CURRENT_RECEIVER_EDITED(k) (k - ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1) #endif @@ -335,6 +339,32 @@ void onBluetoothConnectMenu(const char * result) #define INTERNAL_MODULE_ROWS #endif +#if defined(HARDWARE_EXTERNAL_MODULE) + #define EXTERNAL_MODULE_ROWS \ + LABEL(ExternalModule), \ + MODULE_TYPE_ROWS(EXTERNAL_MODULE), \ + MULTIMODULE_TYPE_ROWS(EXTERNAL_MODULE) /* PROTOCOL */ \ + MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE) /* SUBTYPE */ \ + MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) \ + AFHDS3_PROTOCOL_ROW(EXTERNAL_MODULE) \ + AFHDS3_MODE_ROWS(EXTERNAL_MODULE) \ + MODULE_CHANNELS_ROWS(EXTERNAL_MODULE), \ + IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), /* line reused for PPM: PPM settings */ \ + IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), /* RxNum */ \ + AFHDS3_MODULE_ROWS(EXTERNAL_MODULE) \ + MODULE_POWER_ROW(EXTERNAL_MODULE), \ + IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)), \ + MULTIMODULE_MODULE_ROWS(EXTERNAL_MODULE) \ + FAILSAFE_ROWS(EXTERNAL_MODULE), /* ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE */ \ + IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 1), /* Range check and Register buttons */ \ + IF_PXX2_MODULE(EXTERNAL_MODULE, 0), /* Module options */ \ + IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), /* Receiver 1 */ \ + IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), /* Receiver 2 */ \ + IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), /* Receiver 3 */ +#else + #define EXTERNAL_MODULE_ROWS +#endif + void menuModelSetup(event_t event) { int8_t old_editMode = s_editMode; @@ -370,26 +400,7 @@ void menuModelSetup(event_t event) INTERNAL_MODULE_ROWS - LABEL(ExternalModule), - MODULE_TYPE_ROWS(EXTERNAL_MODULE), - MULTIMODULE_TYPE_ROWS(EXTERNAL_MODULE) // PROTOCOL - MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE) // SUBTYPE - MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) - AFHDS3_PROTOCOL_ROW(EXTERNAL_MODULE) - AFHDS3_MODE_ROWS(EXTERNAL_MODULE) - MODULE_CHANNELS_ROWS(EXTERNAL_MODULE), - IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), // line reused for PPM: PPM settings - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // RxNum - AFHDS3_MODULE_ROWS(EXTERNAL_MODULE) - MODULE_POWER_ROW(EXTERNAL_MODULE), - IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)), - MULTIMODULE_MODULE_ROWS(EXTERNAL_MODULE) - FAILSAFE_ROWS(EXTERNAL_MODULE), //ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 1), // Range check and Register buttons - IF_PXX2_MODULE(EXTERNAL_MODULE, 0), // Module options - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 1 - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 2 - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 3 + EXTERNAL_MODULE_ROWS TRAINER_ROWS }); @@ -912,14 +923,18 @@ void menuModelSetup(event_t event) break; #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL: lcdDrawTextAlignedLeft(y, STR_EXTERNALRF); break; +#endif #if defined(INTERNAL_MODULE_MULTI) case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE: +#endif lcdDrawTextAlignedLeft(y, INDENT TR_MODE); lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_EXTERNAL_MODULE_PROTOCOLS, moduleIdx == EXTERNAL_MODULE ? reusableBuffer.moduleSetup.newType : g_model.moduleData[moduleIdx].type, menuHorizontalPosition==0 ? attr : 0); if (isModuleXJT(moduleIdx)) @@ -1013,7 +1028,9 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_PROTOCOL: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PROTOCOL: +#endif { lcdDrawTextAlignedLeft(y, TR_TYPE); int multi_rfProto = g_model.moduleData[moduleIdx].getMultiProtocol(); @@ -1052,7 +1069,9 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_SUBTYPE: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SUBTYPE: +#endif { lcdDrawTextAlignedLeft(y, STR_SUBTYPE); lcdDrawMultiSubProtocolString(MODEL_SETUP_2ND_COLUMN, y, moduleIdx, g_model.moduleData[moduleIdx].subType, attr); @@ -1095,7 +1114,7 @@ void menuModelSetup(event_t event) lcdDrawTextAlignedLeft(y, INDENT TR_MODE); lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VTRAINERMODES, g_model.trainerData.mode, attr); if (attr) { - g_model.trainerData.mode = checkIncDec(event, g_model.trainerData.mode, 0, TRAINER_MODE_MAX(), EE_MODEL, isTrainerModeAvailable); + g_model.trainerData.mode = checkIncDec(event, g_model.trainerData.mode, TRAINER_MODE_MIN(), TRAINER_MODE_MAX(), EE_MODEL, isTrainerModeAvailable); #if defined(BLUETOOTH) if (checkIncDec_Ret) { bluetooth.state = BLUETOOTH_STATE_OFF; @@ -1182,7 +1201,9 @@ void menuModelSetup(event_t event) #if defined(PCBSKY9X) case ITEM_MODEL_SETUP_EXTRA_MODULE_CHANNELS: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS: +#endif { ModuleData & moduleData = g_model.moduleData[moduleIdx]; lcdDrawTextAlignedLeft(y, STR_CHANNELRANGE); @@ -1308,7 +1329,9 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE: +#endif { ModuleData & moduleData = g_model.moduleData[moduleIdx]; if (isModulePPM(moduleIdx)) { @@ -1462,7 +1485,9 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE: +#endif { ModuleData &moduleData = g_model.moduleData[moduleIdx]; lcdDrawTextAlignedLeft(y, STR_FAILSAFE); @@ -1523,7 +1548,9 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_OPTIONS: +#endif { #if defined(MULTIMODULE) if (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx)) { @@ -1602,7 +1629,9 @@ void menuModelSetup(event_t event) #if defined(INTERNAL_MODULE_MULTI) case ITEM_MODEL_SETUP_INTERNAL_MODULE_POWER: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_POWER: +#endif { auto & module = g_model.moduleData[moduleIdx]; // Lite FCC / Lite FLEX / Lite Pro Flex @@ -1687,7 +1716,9 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND: +#endif if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) { int8_t value = (g_model.moduleData[moduleIdx].multi.optionValue & 0x02) >> 1; lcdDrawText(INDENT_WIDTH, y, STR_MULTI_SERVOFREQ); @@ -1708,13 +1739,17 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_TELEM: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_TELEM: +#endif g_model.moduleData[moduleIdx].multi.disableTelemetry = editCheckBox(g_model.moduleData[moduleIdx].multi.disableTelemetry, MODEL_SETUP_2ND_COLUMN, y, INDENT TR_DISABLE_TELEM, attr, event); break; #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_MAPPING: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_MAPPING: +#endif g_model.moduleData[moduleIdx].multi.disableMapping = editCheckBox(g_model.moduleData[moduleIdx].multi.disableMapping, MODEL_SETUP_2ND_COLUMN, y, INDENT TR_DISABLE_CH_MAP, attr, event); break; #endif @@ -1739,7 +1774,10 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS: #endif - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: { +#if defined(HARDWARE_EXTERNAL_MODULE) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: +#endif + { lcdDrawTextAlignedLeft(y, STR_MODULE_STATUS); char statusText[64]; @@ -1750,7 +1788,10 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS: #endif - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: { +#if defined(HARDWARE_EXTERNAL_MODULE) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: +#endif + { lcdDrawTextAlignedLeft(y, STR_MODULE_SYNC); char statusText[64]; getModuleSyncStatusString(moduleIdx, statusText); @@ -1848,11 +1889,13 @@ void menuModelSetup(event_t event) checkModelIdUnique(g_eeGeneral.currModel, EXTRA_MODULE); break; #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE: case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_MODEL_NUM: if (menuHorizontalPosition == 0) checkModelIdUnique(g_eeGeneral.currModel, EXTERNAL_MODULE); break; +#endif } } } diff --git a/radio/src/gui/128x64/radio_diagkeys.cpp b/radio/src/gui/128x64/radio_diagkeys.cpp index a3d058c3e..803ff7eb7 100644 --- a/radio/src/gui/128x64/radio_diagkeys.cpp +++ b/radio/src/gui/128x64/radio_diagkeys.cpp @@ -49,10 +49,15 @@ void menuRadioDiagKeys(event_t event) displayKeyState(i&1? 20*FW : 18*FW, y, TRM_BASE+i); } - if (i <= KEY_MAX) { - y = MENU_HEADER_HEIGHT + 1 + FH*i; + if (i == 7) { + y = MENU_HEADER_HEIGHT + 1 + FH * 6; + lcdDrawTextAtIndex(8, y, STR_VKEYS, i, 0); + displayKeyState(lcdNextPos + 10, y, i); + } + else if (i <= KEY_MAX) { + y = MENU_HEADER_HEIGHT + 1 + FH * i; lcdDrawTextAtIndex(0, y, STR_VKEYS, i, 0); - displayKeyState(5*FW+2, y, i); + displayKeyState(5 * FW + 2, y, i); } #if defined(PCBSKY9X) diff --git a/radio/src/gui/128x64/radio_setup.cpp b/radio/src/gui/128x64/radio_setup.cpp index e3fd06bb3..26b80cb0f 100644 --- a/radio/src/gui/128x64/radio_setup.cpp +++ b/radio/src/gui/128x64/radio_setup.cpp @@ -79,13 +79,13 @@ enum { ITEM_RADIO_SETUP_MEMORY_WARNING, ITEM_RADIO_SETUP_ALARM_WARNING, ITEM_RADIO_SETUP_RSSI_POWEROFF_ALARM, - ITEM_RADIO_SETUP_BACKLIGHT_LABEL, - ITEM_RADIO_SETUP_BACKLIGHT_MODE, - ITEM_RADIO_SETUP_BACKLIGHT_DELAY, - ITEM_RADIO_SETUP_BRIGHTNESS, + CASE_BACKLIGHT(ITEM_RADIO_SETUP_BACKLIGHT_LABEL) + CASE_BACKLIGHT(ITEM_RADIO_SETUP_BACKLIGHT_MODE) + CASE_BACKLIGHT(ITEM_RADIO_SETUP_BACKLIGHT_DELAY) + CASE_BACKLIGHT(ITEM_RADIO_SETUP_BRIGHTNESS) CASE_PWM_BACKLIGHT(ITEM_RADIO_SETUP_BACKLIGHT_BRIGHTNESS_OFF) CASE_PWM_BACKLIGHT(ITEM_RADIO_SETUP_BACKLIGHT_BRIGHTNESS_ON) - ITEM_RADIO_SETUP_FLASH_BEEP, + CASE_BACKLIGHT(ITEM_RADIO_SETUP_FLASH_BEEP) CASE_SPLASH_PARAM(ITEM_RADIO_SETUP_DISABLE_SPLASH) CASE_PWR_BUTTON_PRESS(ITEM_RADIO_SETUP_PWR_ON_SPEED) CASE_PWR_BUTTON_PRESS(ITEM_RADIO_SETUP_PWR_OFF_SPEED) @@ -152,10 +152,14 @@ void menuRadioSetup(event_t event) CASE_GYRO(0) CASE_GYRO(0) 0, LABEL(ALARMS), 0, CASE_CAPACITY(0) - 0, 0, 0, 0, - LABEL(BACKLIGHT), 0, 0, 0, CASE_PWM_BACKLIGHT(0) + 0, 0, 0, 0, /* ITEM_RADIO_SETUP_INACTIVITY_ALARM ITEM_RADIO_SETUP_MEMORY_WARNING ITEM_RADIO_SETUP_ALARM_WARNING ITEM_RADIO_SETUP_RSSI_POWEROFF_ALARM */ + CASE_BACKLIGHT(LABEL(BACKLIGHT)) + CASE_BACKLIGHT(0) + CASE_BACKLIGHT(0) + CASE_BACKLIGHT(0) CASE_PWM_BACKLIGHT(0) - 0, + CASE_PWM_BACKLIGHT(0) + CASE_BACKLIGHT(0) CASE_SPLASH_PARAM(0) CASE_PWR_BUTTON_PRESS(0) CASE_PWR_BUTTON_PRESS(0) @@ -251,7 +255,7 @@ void menuRadioSetup(event_t event) putsVolts(lcdLastRightPos+FW, y, 120+g_eeGeneral.vBatMax, (menuHorizontalPosition>0 ? attr : 0)|LEFT|NO_UNIT); if (attr && s_editMode>0) { if (menuHorizontalPosition==0) - CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMin, -50, g_eeGeneral.vBatMax+29); // min=4.0V + CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMin, -60, g_eeGeneral.vBatMax+29); // min=3.0V else CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMax, g_eeGeneral.vBatMin-29, +40); // max=16.0V } @@ -411,7 +415,7 @@ void menuRadioSetup(event_t event) case ITEM_RADIO_SETUP_BATTERY_WARNING: lcdDrawTextAlignedLeft(y, STR_BATTERYWARNING); putsVolts(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.vBatWarn, attr|LEFT); - if(attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatWarn, 40, 120); //4-12V + if(attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatWarn, 30, 120); //3-12V break; case ITEM_RADIO_SETUP_MEMORY_WARNING: @@ -450,6 +454,7 @@ void menuRadioSetup(event_t event) if(attr) g_eeGeneral.inactivityTimer = checkIncDec(event, g_eeGeneral.inactivityTimer, 0, 250, EE_GENERAL); //0..250minutes break; +#if defined(BACKLIGHT_GPIO) case ITEM_RADIO_SETUP_BACKLIGHT_LABEL: lcdDrawTextAlignedLeft(y, STR_BACKLIGHT_LABEL); break; @@ -478,6 +483,7 @@ void menuRadioSetup(event_t event) g_eeGeneral.backlightBright = 100 - b; } break; +#endif #if defined(PWM_BACKLIGHT) case ITEM_RADIO_SETUP_BACKLIGHT_BRIGHTNESS_OFF: diff --git a/radio/src/gui/128x64/view_about.cpp b/radio/src/gui/128x64/view_about.cpp index 02e8e4508..b168f91fb 100644 --- a/radio/src/gui/128x64/view_about.cpp +++ b/radio/src/gui/128x64/view_about.cpp @@ -42,7 +42,7 @@ enum AboutScreens { #define ABOUT_X 2 #define ABOUT_INDENT 4 -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEDN) #define EVT_KEY_PREVIOUS_VIEW EVT_KEY_BREAK(KEY_PAGEUP) #define EVT_KEY_NEXT_VIEW EVT_KEY_BREAK(KEY_PAGEDN) #elif defined(NAVIGATION_X7) diff --git a/radio/src/gui/128x64/view_channels.cpp b/radio/src/gui/128x64/view_channels.cpp index f71d833c1..ffb4d9b33 100644 --- a/radio/src/gui/128x64/view_channels.cpp +++ b/radio/src/gui/128x64/view_channels.cpp @@ -26,7 +26,12 @@ constexpr coord_t CHANNEL_GAUGE_OFFSET = CHANNEL_VALUE_OFFSET; constexpr coord_t CHANNEL_BAR_WIDTH = 70; constexpr coord_t CHANNEL_PROPERTIES_OFFSET = CHANNEL_GAUGE_OFFSET + CHANNEL_BAR_WIDTH + 2; -#if defined(NAVIGATION_X7_TX12) +#if defined(RADIO_T8) +#define EVT_KEY_PREVIOUS_VIEW EVT_KEY_BREAK(KEY_PAGEUP) +#define EVT_KEY_NEXT_VIEW EVT_KEY_BREAK(KEY_PAGEDN) +#define EVT_KEY_NEXT_PAGE EVT_KEY_BREAK(KEY_PLUS) +#define EVT_KEY_PREVIOUS_PAGE EVT_KEY_BREAK(KEY_MINUS) +#elif defined(NAVIGATION_X7_TX12) #define EVT_KEY_PREVIOUS_VIEW EVT_KEY_BREAK(KEY_PAGEUP) #define EVT_KEY_NEXT_VIEW EVT_KEY_BREAK(KEY_PAGEDN) #define EVT_KEY_NEXT_PAGE EVT_ROTARY_RIGHT diff --git a/radio/src/gui/128x64/view_main.cpp b/radio/src/gui/128x64/view_main.cpp index ee486269b..b3cea8de7 100644 --- a/radio/src/gui/128x64/view_main.cpp +++ b/radio/src/gui/128x64/view_main.cpp @@ -93,8 +93,9 @@ void doMainScreenGraphics() if (g_model.throttleReversed && CONVERT_MODE(2) == THR_STICK) calibStickVert = -calibStickVert; drawStick(RBOX_CENTERX, calibratedAnalogs[CONVERT_MODE(3)], calibStickVert); - +#if defined(HARDWARE_POT1) drawPotsBars(); +#endif } void displayTrims(uint8_t phase) @@ -213,7 +214,16 @@ void displayVoltageOrAlarm() #define displayVoltageOrAlarm() displayBattVoltage() #endif -#if defined(NAVIGATION_X7_TX12) +#if defined(RADIO_T8) +#define EVT_KEY_CONTEXT_MENU EVT_KEY_LONG(KEY_ENTER) +#define EVT_KEY_PREVIOUS_VIEW EVT_KEY_BREAK(KEY_PAGEUP) +#define EVT_KEY_NEXT_VIEW EVT_KEY_FIRST(KEY_PAGEDN) +#define EVT_KEY_NEXT_PAGE EVT_KEY_FIRST(KEY_PLUS) +#define EVT_KEY_PREVIOUS_PAGE EVT_KEY_FIRST(KEY_MINUS) +#define EVT_KEY_MODEL_MENU EVT_KEY_LONG(KEY_MODEL) +#define EVT_KEY_GENERAL_MENU EVT_KEY_LONG(KEY_SYS) +#define EVT_KEY_TELEMETRY EVT_KEY_LONG(KEY_PAGEUP) +#elif defined(NAVIGATION_X7_TX12) #define EVT_KEY_CONTEXT_MENU EVT_KEY_LONG(KEY_ENTER) #define EVT_KEY_PREVIOUS_VIEW EVT_KEY_FIRST(KEY_PAGEUP) #define EVT_KEY_NEXT_VIEW EVT_KEY_FIRST(KEY_PAGEDN) @@ -410,6 +420,7 @@ void menuMainView(event_t event) killEvents(event); break; + case EVT_KEY_FIRST(KEY_EXIT): #if defined(GVARS) if (gvarDisplayTimer > 0) { diff --git a/radio/src/gui/128x64/view_statistics.cpp b/radio/src/gui/128x64/view_statistics.cpp index 2b46be36e..0765cf9b6 100644 --- a/radio/src/gui/128x64/view_statistics.cpp +++ b/radio/src/gui/128x64/view_statistics.cpp @@ -31,7 +31,7 @@ void menuStatisticsView(event_t event) switch (event) { case EVT_KEY_FIRST(KEY_UP): -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEDN) case EVT_KEY_BREAK(KEY_PAGEDN): #elif defined(NAVIGATION_X7) case EVT_KEY_BREAK(KEY_PAGE): @@ -41,7 +41,7 @@ void menuStatisticsView(event_t event) break; case EVT_KEY_FIRST(KEY_DOWN): -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEUP) case EVT_KEY_BREAK(KEY_PAGEUP): killEvents(event); chainMenu(menuStatisticsDebug2); @@ -140,7 +140,7 @@ void menuStatisticsDebug(event_t event) break; case EVT_KEY_FIRST(KEY_UP): -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEDN) case EVT_KEY_BREAK(KEY_PAGEDN): disableVBatBridge(); chainMenu(menuStatisticsDebug2); @@ -153,7 +153,7 @@ void menuStatisticsDebug(event_t event) #endif case EVT_KEY_FIRST(KEY_DOWN): -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEUP) case EVT_KEY_BREAK(KEY_PAGEUP): #elif defined(NAVIGATION_X7) case EVT_KEY_LONG(KEY_PAGE): @@ -278,7 +278,7 @@ void menuStatisticsDebug2(event_t event) break; case EVT_KEY_FIRST(KEY_UP): -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEDN) case EVT_KEY_BREAK(KEY_PAGEDN): #elif defined(NAVIGATION_X7) case EVT_KEY_BREAK(KEY_PAGE): @@ -287,7 +287,7 @@ void menuStatisticsDebug2(event_t event) return; case EVT_KEY_FIRST(KEY_DOWN): -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEUP) case EVT_KEY_BREAK(KEY_PAGEUP): #elif defined(NAVIGATION_X7) case EVT_KEY_LONG(KEY_PAGE): diff --git a/radio/src/gui/common/stdlcd/radio_hardware.cpp b/radio/src/gui/common/stdlcd/radio_hardware.cpp index fca6e047c..1bc66768d 100644 --- a/radio/src/gui/common/stdlcd/radio_hardware.cpp +++ b/radio/src/gui/common/stdlcd/radio_hardware.cpp @@ -195,7 +195,9 @@ enum { ITEM_RADIO_HARDWARE_MAX }; -#if (NUM_POTS + NUM_SLIDERS) == 1 +#if (NUM_POTS + NUM_SLIDERS) == 0 + #define POTS_ROWS HIDDEN_ROW +#elif (NUM_POTS + NUM_SLIDERS) == 1 #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1 #elif (NUM_POTS + NUM_SLIDERS) == 2 #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 @@ -238,20 +240,20 @@ enum { #endif #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) -#define EXTERNAL_ANTENNA_ROW 0, -void onHardwareAntennaSwitchConfirm(const char * result) -{ - if (result == STR_OK) { - // Switch to external antenna confirmation - g_eeGeneral.antennaMode = reusableBuffer.radioHardware.antennaMode; - storageDirty(EE_GENERAL); + #define EXTERNAL_ANTENNA_ROW 0, + void onHardwareAntennaSwitchConfirm(const char * result) + { + if (result == STR_OK) { + // Switch to external antenna confirmation + g_eeGeneral.antennaMode = reusableBuffer.radioHardware.antennaMode; + storageDirty(EE_GENERAL); + } + else { + reusableBuffer.radioHardware.antennaMode = g_eeGeneral.antennaMode; + } } - else { - reusableBuffer.radioHardware.antennaMode = g_eeGeneral.antennaMode; - } -} #else -#define EXTERNAL_ANTENNA_ROW + #define EXTERNAL_ANTENNA_ROW #endif #if defined(PCBX9LITE) @@ -262,7 +264,7 @@ void onHardwareAntennaSwitchConfirm(const char * result) #define SWITCH_TYPE_MAX(sw) (SWITCH_3POS) #elif defined(PCBX9E) #define SWITCH_TYPE_MAX(sw) ((MIXSRC_SF - MIXSRC_FIRST_SWITCH == sw || MIXSRC_SH - MIXSRC_FIRST_SWITCH == sw) ? SWITCH_2POS : SWITCH_3POS) -#elif defined(RADIO_TX12) +#elif defined(RADIO_TX12) || defined(RADIO_T8) #define SWITCH_TYPE_MAX(sw) ((MIXSRC_SA - MIXSRC_FIRST_SWITCH == sw || MIXSRC_SD - MIXSRC_FIRST_SWITCH == sw) ? SWITCH_2POS : SWITCH_3POS) #else #define SWITCH_TYPE_MAX(sw) ((MIXSRC_SF - MIXSRC_FIRST_SWITCH == sw || MIXSRC_SH - MIXSRC_FIRST_SWITCH <= sw) ? SWITCH_2POS : SWITCH_3POS) @@ -322,35 +324,27 @@ void menuRadioHardware(event_t event) MENU(STR_HARDWARE, menuTabGeneral, MENU_RADIO_HARDWARE, HEADER_LINE + ITEM_RADIO_HARDWARE_MAX, { HEADER_LINE_COLUMNS 0 /* calibration button */, - 0 /* stick 1 */, - 0 /* stick 2 */, - 0 /* stick 3 */, - 0 /* stick 4 */, + 0 /* stick 1 */, + 0 /* stick 2 */, + 0 /* stick 3 */, + 0 /* stick 4 */, LABEL(Pots), - POTS_ROWS, + POTS_ROWS, LABEL(Switches), - SWITCHES_ROWS, - + SWITCHES_ROWS, 0 /* battery calib */, RTC_ROW TX_CAPACITY_MEASUREMENT_ROWS - MAX_BAUD_ROWS - BLUETOOTH_ROWS - EXTERNAL_ANTENNA_ROW - AUX_SERIAL_ROWS - 0 /* ADC filter */, READONLY_ROW /* RAS */, SPORT_POWER_ROWS 1 /* debugs */, - - 0, - - 0 + 0 /* EEPROM backup */, + 0 /* Factory reset */ }); uint8_t sub = menuVerticalPosition - HEADER_LINE; diff --git a/radio/src/gui/common/stdlcd/view_telemetry.cpp b/radio/src/gui/common/stdlcd/view_telemetry.cpp index ecdb96aa9..07a31442c 100644 --- a/radio/src/gui/common/stdlcd/view_telemetry.cpp +++ b/radio/src/gui/common/stdlcd/view_telemetry.cpp @@ -34,7 +34,7 @@ enum NavigationDirection { #if defined(NAVIGATION_XLITE) #define EVT_KEY_PREVIOUS_VIEW(evt) (evt == EVT_KEY_LONG(KEY_LEFT) && IS_SHIFT_PRESSED()) #define EVT_KEY_NEXT_VIEW(evt) (evt == EVT_KEY_LONG(KEY_RIGHT) && IS_SHIFT_PRESSED()) -#elif defined(NAVIGATION_X7_TX12) +#elif defined(KEYS_GPIO_REG_PAGEDN) #define EVT_KEY_PREVIOUS_VIEW(evt) (evt == EVT_KEY_FIRST(KEY_PAGEUP)) #define EVT_KEY_NEXT_VIEW(evt) (evt == EVT_KEY_FIRST(KEY_PAGEDN)) #elif defined(NAVIGATION_X7) || defined(NAVIGATION_X9D) diff --git a/radio/src/gui/navigation/navigation_x7.cpp b/radio/src/gui/navigation/navigation_x7.cpp index ac4a4bbdd..311a53d9f 100644 --- a/radio/src/gui/navigation/navigation_x7.cpp +++ b/radio/src/gui/navigation/navigation_x7.cpp @@ -56,8 +56,11 @@ void onSwitchLongEnterPress(const char * result) int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_flags, IsValueAvailable isValueAvailable, const CheckIncDecStops &stops) { int newval = val; - +#if defined(ROTARY_ENCODER_NAVIGATION) if (s_editMode>0 && event==EVT_ROTARY_RIGHT) { +#else + if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_MINUS) || event==EVT_KEY_REPT(KEY_MINUS))) { +#endif newval += min(rotencSpeed, i_max-val); while (isValueAvailable && !isValueAvailable(newval) && newval<=i_max) { newval++; @@ -67,7 +70,11 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla AUDIO_KEY_ERROR(); } } +#if defined(ROTARY_ENCODER_NAVIGATION) else if (s_editMode>0 && event==EVT_ROTARY_LEFT) { +#else + if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_PLUS) || event==EVT_KEY_REPT(KEY_PLUS))) { +#endif newval -= min(rotencSpeed, val-i_min); while (isValueAvailable && !isValueAvailable(newval) && newval>=i_min) { newval--; @@ -217,7 +224,7 @@ void check(event_t event, uint8_t curr, const MenuHandlerFunc * menuTab, uint8_t if (menuTab) { int cc = curr; switch (event) { -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEUP) case EVT_KEY_FIRST(KEY_PAGEUP): #else case EVT_KEY_LONG(KEY_PAGE): @@ -232,7 +239,7 @@ void check(event_t event, uint8_t curr, const MenuHandlerFunc * menuTab, uint8_t killEvents(event); break; -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEDN) case EVT_KEY_FIRST(KEY_PAGEDN): #else case EVT_KEY_BREAK(KEY_PAGE): @@ -310,12 +317,14 @@ void check(event_t event, uint8_t curr, const MenuHandlerFunc * menuTab, uint8_t } } break; - +#if defined(ROTARY_ENCODER_NAVIGATION) case EVT_ROTARY_RIGHT: - case EVT_KEY_FIRST(KEY_RIGHT): AUDIO_KEY_PRESS(); // no break - case EVT_KEY_REPT(KEY_RIGHT): +#else + case EVT_KEY_FIRST(KEY_DOWN): + case EVT_KEY_REPT(KEY_DOWN): +#endif if (s_editMode > 0) break; // TODO it was != if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) { if (l_posHorz >= 0) { @@ -341,12 +350,14 @@ void check(event_t event, uint8_t curr, const MenuHandlerFunc * menuTab, uint8_t l_posHorz = POS_HORZ_INIT(l_posVert); break; - +#if defined(ROTARY_ENCODER_NAVIGATION) case EVT_ROTARY_LEFT: - case EVT_KEY_FIRST(KEY_LEFT): AUDIO_KEY_PRESS(); // no break - case EVT_KEY_REPT(KEY_LEFT): +#else + case EVT_KEY_FIRST(KEY_UP): + case EVT_KEY_REPT(KEY_UP): +#endif if (s_editMode > 0) break; // TODO it was != if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) { if (l_posHorz >= 0) { diff --git a/radio/src/keys.h b/radio/src/keys.h index 44c1e53fe..0d4bc37dc 100644 --- a/radio/src/keys.h +++ b/radio/src/keys.h @@ -82,6 +82,11 @@ inline bool IS_KEY_EVT(event_t evt, uint8_t key) #define EVT_ROTARY_RIGHT 0xDE00 #define IS_NEXT_EVENT(event) (event==EVT_KEY_FIRST(KEY_DOWN) || event==EVT_KEY_REPT(KEY_DOWN)) #define IS_PREVIOUS_EVENT(event) (event==EVT_KEY_FIRST(KEY_UP) || event==EVT_KEY_REPT(KEY_UP)) +#elif defined(RADIO_T8) + #define EVT_ROTARY_BREAK EVT_KEY_BREAK(KEY_ENTER) + #define EVT_ROTARY_LONG EVT_KEY_LONG(KEY_ENTER) + #define IS_NEXT_EVENT(event) (event==EVT_KEY_FIRST(KEY_DOWN) || event==EVT_KEY_REPT(KEY_DOWN)) + #define IS_PREVIOUS_EVENT(event) (event==EVT_KEY_FIRST(KEY_UP) || event==EVT_KEY_REPT(KEY_UP)) #elif (defined(PCBHORUS) || defined(PCBTARANIS)) && defined(ROTARY_ENCODER_NAVIGATION) #define EVT_ROTARY_BREAK EVT_KEY_BREAK(KEY_ENTER) #define EVT_ROTARY_LONG EVT_KEY_LONG(KEY_ENTER) diff --git a/radio/src/lua/CMakeLists.txt b/radio/src/lua/CMakeLists.txt index 5b89c1f65..fee1d9f15 100644 --- a/radio/src/lua/CMakeLists.txt +++ b/radio/src/lua/CMakeLists.txt @@ -29,5 +29,6 @@ if(PYTHONINTERP_FOUND) add_lua_export_target(x12s ${LUA_INCLUDES} -DPCBHORUS -DPCBX12S) add_lua_export_target(t12 ${LUA_INCLUDES} -DPCBTARANIS -DPCBX7 -DRADIO_T12) add_lua_export_target(tx12 ${LUA_INCLUDES} -DPCBTARANIS -DPCBX7 -DRADIO_TX12) + add_lua_export_target(t8 ${LUA_INCLUDES} -DPCBTARANIS -DPCBX7 -DRADIO_T8) add_lua_export_target(t16 ${LUA_INCLUDES} -DPCBHORUS -DPCBX10 -DRADIO_T16) endif() diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 37634ae1c..b3c3f5ee8 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -42,6 +42,8 @@ #include "lua/lua_exports_t12.inc" #elif defined(RADIO_TX12) #include "lua/lua_exports_tx12.inc" +#elif defined(RADIO_T8) + #include "lua/lua_exports_t8.inc" #elif defined(PCBX9LITES) #include "lua/lua_exports_x9lites.inc" #elif defined(PCBX9LITE) @@ -1779,11 +1781,11 @@ const luaR_value_entry opentxConstants[] = { { "MIXSRC_SE", MIXSRC_SE }, { "MIXSRC_SG", MIXSRC_SG }, #endif -#if !defined(PCBXLITE) && !defined(PCBX9LITE) +#if defined(HARDWARE_SWITCH_F) { "MIXSRC_SF", MIXSRC_SF }, -#if !defined(RADIO_TX12) - { "MIXSRC_SH", MIXSRC_SH }, #endif +#if defined(HARDWARE_SWITCH_H) + { "MIXSRC_SH", MIXSRC_SH }, #endif { "MIXSRC_CH1", MIXSRC_CH1 }, { "SWSRC_LAST", SWSRC_LAST_LOGICAL_SWITCH }, @@ -1845,7 +1847,7 @@ const luaR_value_entry opentxConstants[] = { { "ROTENC_LOWSPEED", ROTENC_LOWSPEED }, { "ROTENC_MIDSPEED", ROTENC_MIDSPEED }, { "ROTENC_HIGHSPEED", ROTENC_HIGHSPEED }, -#elif defined(PCBX9D) || defined(PCBX9DP) // key reverted between field nav and value change +#elif defined(PCBX9D) || defined(PCBX9DP) || defined(RADIO_T8) // key reverted between field nav and value change { "EVT_VIRTUAL_PREV", EVT_KEY_FIRST(KEY_PLUS) }, { "EVT_VIRTUAL_PREV_REPT", EVT_KEY_REPT(KEY_PLUS) }, { "EVT_VIRTUAL_NEXT", EVT_KEY_FIRST(KEY_MINUS) }, @@ -1882,7 +1884,7 @@ const luaR_value_entry opentxConstants[] = { { "EVT_VIRTUAL_ENTER_LONG", EVT_KEY_LONG(KEY_ENTER) }, { "EVT_VIRTUAL_EXIT", EVT_KEY_BREAK(KEY_EXIT) }, #elif defined(NAVIGATION_X7) || defined(NAVIGATION_X9D) -#if defined(RADIO_TX12) +#if defined(RADIO_TX12) || defined(RADIO_T8) { "EVT_VIRTUAL_PREV_PAGE", EVT_KEY_BREAK(KEY_PAGEUP) }, { "EVT_VIRTUAL_NEXT_PAGE", EVT_KEY_BREAK(KEY_PAGEDN) }, { "EVT_VIRTUAL_MENU", EVT_KEY_BREAK(KEY_MODEL) }, diff --git a/radio/src/main.cpp b/radio/src/main.cpp index db12ee20f..8ff9cd736 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -531,6 +531,10 @@ void perMain() } #endif +#if defined(KEYS_GPIO_REG_BIND) && defined(BIND_KEY) + bindButtonHandler(evt); +#endif + #if defined(GUI) DEBUG_TIMER_START(debugTimerGuiMain); guiMain(evt); diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 73b39c407..7a222635a 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -334,6 +334,12 @@ void generalDefault() setDefaultOwnerId(); #endif +#if defined(RADIOMASTER_RTF_RELEASE) + // Those settings are for headless radio + g_eeGeneral.USBMode = USB_JOYSTICK_MODE; + g_eeGeneral.disableRtcWarning = 1; +#endif + g_eeGeneral.chkSum = 0xFFFF; } @@ -523,6 +529,15 @@ void modelDefault(uint8_t id) g_model.switchWarningState |= (1 << (3*i)); } #endif + +#if defined(RADIOMASTER_RTF_RELEASE) + // Those settings are for headless radio + g_model.trainerData.mode = TRAINER_MODE_SLAVE; + g_model.moduleData[INTERNAL_MODULE].type = MODULE_TYPE_MULTIMODULE; + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKY); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKY_SUBTYPE_D8; + g_model.moduleData[INTERNAL_MODULE].failsafeMode = FAILSAFE_NOPULSES; +#endif } bool isInputRecursive(int index) @@ -917,7 +932,7 @@ void checkAll() checkFailsafe(); checkRSSIAlarmsDisabled(); -#if defined(SDCARD) +#if defined(SDCARD) && !defined(RADIOMASTER_RTF_RELEASE) checkSDVersion(); #endif @@ -2183,6 +2198,7 @@ uint32_t pwrCheck() #endif event_t evt = getEvent(false); DISPLAY_WARNING(evt); + LED_ERROR_BEGIN(); lcdRefresh(); if (warningResult) { @@ -2192,6 +2208,7 @@ uint32_t pwrCheck() else if (!warningText) { // shutdown has been cancelled pwr_check_state = PWR_CHECK_PAUSED; + LED_ERROR_END(); return e_power_on; } } diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 52a51719b..bb756305b 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -69,6 +69,12 @@ #define CASE_GYRO(x) #endif +#if defined(BACKLIGHT_GPIO) +#define CASE_BACKLIGHT(x) x, +#else +#define CASE_BACKLIGHT(x) +#endif + #if defined(LUA) #define CASE_LUA(x) x, #else @@ -382,6 +388,8 @@ inline bool SPLASH_NEEDED() #define ROTENC_HIGHSPEED 50 #define ROTENC_DELAY_MIDSPEED 32 #define ROTENC_DELAY_HIGHSPEED 16 +#elif defined(RADIO_T8) + constexpr uint8_t rotencSpeed = 1; #endif constexpr uint8_t HEART_TIMER_10MS = 0x01; @@ -437,6 +445,10 @@ extern uint8_t potsPos[NUM_XPOTS]; bool trimDown(uint8_t idx); void readKeysAndTrims(); +#if defined(KEYS_GPIO_REG_BIND) +void bindButtonHandler(event_t event); +#endif + uint16_t evalChkSum(); void alert(const char * title, const char * msg, uint8_t sound); @@ -998,7 +1010,13 @@ constexpr uint8_t OPENTX_START_NO_CHECKS = 0x04; #if defined(STATUS_LEDS) #define LED_ERROR_BEGIN() ledRed() +#if defined(RADIO_T8) + // Because of green backlit logo, green is preferred on this radio + #define LED_ERROR_END() ledGreen() + #define LED_BIND() ledBlue() +#else #define LED_ERROR_END() ledBlue() +#endif #else #define LED_ERROR_BEGIN() #define LED_ERROR_END() diff --git a/radio/src/options.h b/radio/src/options.h index acadfd129..4f1a1a077 100644 --- a/radio/src/options.h +++ b/radio/src/options.h @@ -97,6 +97,9 @@ static const char * const options[] = { #endif #if defined(HARDWARE_EXTERNAL_ACCESS_MOD) "externalaccessmod", +#endif +#if defined(BIND_KEY) + "bindkey", #endif nullptr //sentinel }; diff --git a/radio/src/pulses/modules_constants.h b/radio/src/pulses/modules_constants.h index 15a2c2283..6fbec157e 100644 --- a/radio/src/pulses/modules_constants.h +++ b/radio/src/pulses/modules_constants.h @@ -203,6 +203,14 @@ enum MMRFrskySubtypes { MM_RF_FRSKY_SUBTYPE_D16_CLONED }; +enum MMRFrskyX2Subtypes { + MM_RF_FRSKYX2_SUBTYPE_D16, + MM_RF_FRSKYX2_SUBTYPE_D8, + MM_RF_FRSKYX2_SUBTYPE_D16_LBT, + MM_RF_FRSKYX2_SUBTYPE_D16_LBT_8CH, + MM_RF_FRSKYX2_SUBTYPE_D16_CLONED +}; + enum ModuleSubtypeDSM2 { DSM2_PROTO_LP45, DSM2_PROTO_DSM2, diff --git a/radio/src/sdcard.h b/radio/src/sdcard.h index fc9a06b92..b6888f1f8 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -145,6 +145,8 @@ const char * getBasename(const char * path); #define OTX_FOURCC 0x3D78746F // otx for Jumper T12 #elif defined(RADIO_TX12) #define OTX_FOURCC 0x4178746F // otx for Radiomaster TX12 +#elif defined(RADIO_T8) + #define OTX_FOURCC 0x4378746F // otx for Radiomaster T8 #elif defined(PCBX7) #define OTX_FOURCC 0x3678746F // otx for Taranis X7 / X7S / X7 Express / X7S Express #elif defined(PCBX9LITES) diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index 6d086e100..2e9c6a89e 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -273,6 +273,15 @@ void OpenTxSim::updateKeysAndSwitches(bool start) KEY_Down, KEY_EXIT, KEY_Right, KEY_TELE, KEY_Left, KEY_SYS, +#elif defined(RADIO_T8) + KEY_Page_Up, KEY_PAGEUP, + KEY_Page_Down, KEY_PAGEDN, + KEY_Return, KEY_ENTER, + KEY_Right, KEY_MODEL, + KEY_BackSpace, KEY_EXIT, + KEY_Left, KEY_SYS, + KEY_Up, KEY_PLUS, + KEY_Down, KEY_MINUS, #elif defined(PCBTARANIS) KEY_Page_Up, KEY_MENU, #if defined(KEYS_GPIO_REG_PAGE) diff --git a/radio/src/targets/common/arm/stm32/adc_driver.cpp b/radio/src/targets/common/arm/stm32/adc_driver.cpp index c6f5ee182..c6dddda75 100644 --- a/radio/src/targets/common/arm/stm32/adc_driver.cpp +++ b/radio/src/targets/common/arm/stm32/adc_driver.cpp @@ -125,6 +125,9 @@ void adcInit() ADC_MAIN->SQR2 = (ADC_CHANNEL_BATT << 0) + (ADC_Channel_Vbat << 5); ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15) + (ADC_CHANNEL_POT1 << 20) + (ADC_CHANNEL_POT2 << 25); } +#elif defined(RADIO_T8) + ADC_MAIN->SQR2 = (ADC_CHANNEL_BATT << 0) + (ADC_Channel_Vbat << 5); + ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15); #elif defined(PCBX7) ADC_MAIN->SQR2 = (ADC_CHANNEL_BATT << 0) + (ADC_Channel_Vbat << 5); ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15) + (ADC_CHANNEL_POT1 << 20) + (ADC_CHANNEL_POT2 << 25); diff --git a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt index 55bcdeb66..1a69afdff 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt +++ b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt @@ -89,7 +89,7 @@ set(BOOTLOADER_SRC bin_files.cpp ) -if(PCB STREQUAL X12S OR PCB STREQUAL X10 OR PCB STREQUAL X9E OR (PCB STREQUAL X9D+ AND PCBREV STREQUAL 2019) OR (PCB STREQUAL X7 AND NOT (PCBREV STREQUAL T12)) OR PCB STREQUAL X9LITE OR PCB STREQUAL X9LITES) +if(PCB STREQUAL X12S OR PCB STREQUAL X10 OR PCB STREQUAL X9E OR (PCB STREQUAL X9D+ AND PCBREV STREQUAL 2019) OR (PCB STREQUAL X7 AND NOT (PCBREV STREQUAL T12 OR PCBREV STREQUAL T8)) OR PCB STREQUAL X9LITE OR PCB STREQUAL X9LITES) set(BOOTLOADER_SRC ${BOOTLOADER_SRC} ../../../../../targets/common/arm/stm32/rotary_encoder_driver.cpp diff --git a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp index b849fa8a2..e94ae4756 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp +++ b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp @@ -218,8 +218,13 @@ int main() } } +#if defined(RADIO_T8) && !defined(RADIOMASTER_RELEASE) + // Bind button not pressed + if ((~KEYS_GPIO_REG_BIND & KEYS_GPIO_PIN_BIND) == false) { +#else // LHR & RHL trims not pressed simultanously if (readTrims() != BOOTLOADER_KEYS) { +#endif // Start main application jumpTo(APP_START_ADDRESS); } diff --git a/radio/src/targets/common/arm/stm32/pwr_driver.cpp b/radio/src/targets/common/arm/stm32/pwr_driver.cpp index 220b5fdfd..32ff59195 100644 --- a/radio/src/targets/common/arm/stm32/pwr_driver.cpp +++ b/radio/src/targets/common/arm/stm32/pwr_driver.cpp @@ -80,7 +80,7 @@ void pwrInit() GPIO_Init(SD_PRESENT_GPIO, &GPIO_InitStructure); #endif -#if defined(INTMODULE_USART) && defined(TRAINER_MODULE_CPPM_GPIO_PIN) +#if defined(INTMODULE_USART) && defined(TRAINER_MODULE_CPPM) GPIO_SetBits(TRAINER_MODULE_CPPM_GPIO, TRAINER_MODULE_CPPM_GPIO_PIN); GPIO_InitStructure.GPIO_Pin = TRAINER_MODULE_CPPM_GPIO_PIN; GPIO_Init(TRAINER_MODULE_CPPM_GPIO, &GPIO_InitStructure); diff --git a/radio/src/targets/sky9x/CMakeLists.txt b/radio/src/targets/sky9x/CMakeLists.txt index 842035bde..d09906965 100644 --- a/radio/src/targets/sky9x/CMakeLists.txt +++ b/radio/src/targets/sky9x/CMakeLists.txt @@ -12,6 +12,7 @@ set(PPM_LIMITS_SYMETRICAL YES) set(PXX1 YES) set(AFHDS3 NO) set(GHOST NO) +add_definitions(-DHARDWARE_EXTERNAL_MODULE) add_definitions(-DDISABLE_MULTI_UPDATE) if(PCB STREQUAL 9XRPRO) diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index a287605df..718e3c684 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -6,6 +6,9 @@ option(AFHDS3 "AFHDS3 TX Module" ON) option(GHOST "Ghost TX Module" ON) option(INTERNAL_MODULE_PPM "Support for PPM internal module" OFF) option(AUTOUPDATE "Auto update internal chips from SD" OFF) +option(BIND_KEY "Enable bind button" OFF) + +set(EXTERNAL_MODULE YES) if(PCB STREQUAL X9E) option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" ON) @@ -144,6 +147,24 @@ elseif(PCB STREQUAL X7) add_definitions(-DRADIO_TX12) add_definitions(-DEEPROM_VARIANT=0x4002) add_definitions(-DMANUFACTURER_RADIOMASTER) + elseif(PCBREV STREQUAL T8) + option(INTERNAL_MODULE_MULTI "Support for MULTI internal module" ON) + set(EXTERNAL_MODULE NO) + set(AFHDS3 NO) + set(FLAVOUR t8) + set(NAVIGATION_TYPE x7) + set(CPU_TYPE STM32F2) + set(CPU_TYPE_FULL STM32F205xE) # for size report + set(LINKER_SCRIPT targets/taranis/stm32f2_flash.ld) + set(ROTARY_ENCODER NO) + set(LUA_EXPORT lua_export_t8) + set(BLUETOOTH NO) + if(RADIOMASTER_RELEASE) + set(BIND_KEY YES) + endif() + add_definitions(-DRADIO_T8) + add_definitions(-DEEPROM_VARIANT=0x4004) + add_definitions(-DMANUFACTURER_RADIOMASTER) elseif(PCBREV STREQUAL ACCESS) option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" OFF) option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" ON) @@ -334,6 +355,14 @@ if(ROTARY_ENCODER) ) endif() +if(BIND_KEY) + set(TARGET_SRC + ${TARGET_SRC} + bind_button_driver.cpp + ) + add_definitions(-DBIND_KEY) +endif() + if(USB_CHARGER) set(TARGET_SRC ${TARGET_SRC} usb_charger_driver.cpp) add_definitions(-DUSB_CHARGER) @@ -454,4 +483,8 @@ if(INTERNAL_MODULE_PPM) add_definitions(-DINTERNAL_MODULE_PPM) endif() +if(EXTERNAL_MODULE) + add_definitions(-DHARDWARE_EXTERNAL_MODULE) +endif() + set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET}) diff --git a/radio/src/targets/taranis/backlight_driver.cpp b/radio/src/targets/taranis/backlight_driver.cpp index 63a5bfd1a..7c758f818 100644 --- a/radio/src/targets/taranis/backlight_driver.cpp +++ b/radio/src/targets/taranis/backlight_driver.cpp @@ -20,7 +20,13 @@ #include "opentx.h" -#if defined(PCBX9E) +#if !defined(BACKLIGHT_GPIO) + // no backlight + void backlightInit() {} + void backlightEnable(uint8_t level) {} + void backlightDisable() {} + uint8_t isBacklightEnabled() {return false;} +#elif defined(PCBX9E) void backlightInit() { GPIO_InitTypeDef GPIO_InitStructure; diff --git a/radio/src/targets/taranis/bind_button_driver.cpp b/radio/src/targets/taranis/bind_button_driver.cpp new file mode 100644 index 000000000..2d3ca63fc --- /dev/null +++ b/radio/src/targets/taranis/bind_button_driver.cpp @@ -0,0 +1,100 @@ +/* + * 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 "opentx.h" + + +bool setBindProtocolSelection() +{ + int16_t xPos = calibratedAnalogs[CONVERT_MODE(0)]; + int16_t yPos = calibratedAnalogs[CONVERT_MODE(1)]; + + // Center: D8 + if (abs(xPos < 50 && abs(yPos) < 50)) { + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKY); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKY_SUBTYPE_D8; + storageDirty(EE_MODEL); + return true; + } + + // Top left: D16 FCC + if (xPos < -512 && yPos > 512) { + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKY); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKY_SUBTYPE_D16; + g_model.moduleData[INTERNAL_MODULE].failsafeMode = FAILSAFE_NOPULSES; + storageDirty(EE_MODEL); + return true; + } + + // Top right: D16 LBT + if (xPos > 512 && yPos > 512) { + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKY); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKY_SUBTYPE_D16_LBT; + g_model.moduleData[INTERNAL_MODULE].failsafeMode = FAILSAFE_NOPULSES; + storageDirty(EE_MODEL); + return true; + } + + // Bottom left: V2.1 D16 FCC + if (xPos < -512 && yPos < -512) { + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKYX2); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKYX2_SUBTYPE_D16; + g_model.moduleData[INTERNAL_MODULE].failsafeMode = FAILSAFE_NOPULSES; + storageDirty(EE_MODEL); + return true; + } + + // Bottom right: V2.1 D16 LBT + if (xPos > 512 && yPos < -512) { + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKYX2); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKYX2_SUBTYPE_D16_LBT; + g_model.moduleData[INTERNAL_MODULE].failsafeMode = FAILSAFE_NOPULSES; + storageDirty(EE_MODEL); + return true; + } + + return false; +} + +void bindButtonHandler(event_t event) +{ + if( !isModuleMultimodule(INTERNAL_MODULE)) + return; + + if (event == EVT_KEY_LONG(KEY_BIND) && getMultiBindStatus(INTERNAL_MODULE) == MULTI_NORMAL_OPERATION) { + if (setBindProtocolSelection()) { + setMultiBindStatus(INTERNAL_MODULE, MULTI_BIND_INITIATED); + moduleState[INTERNAL_MODULE].mode = MODULE_MODE_BIND; + } + } + + if (getMultiBindStatus(INTERNAL_MODULE) == MULTI_BIND_INITIATED) { + if (FAST_BLINK_ON_PHASE) + LED_BIND(); + else + ledOff(); + } + + if (getMultiBindStatus(INTERNAL_MODULE) == MULTI_BIND_FINISHED) { + setMultiBindStatus(INTERNAL_MODULE, MULTI_NORMAL_OPERATION); + moduleState[INTERNAL_MODULE].mode = MODULE_MODE_NORMAL; + LED_ERROR_END(); + } +} diff --git a/radio/src/targets/taranis/board.cpp b/radio/src/targets/taranis/board.cpp index d86a82f2d..bec585b25 100644 --- a/radio/src/targets/taranis/board.cpp +++ b/radio/src/targets/taranis/board.cpp @@ -272,6 +272,8 @@ void boardOff() #if defined (RADIO_TX12) #define BATTERY_DIVIDER 22830 +#elif defined (RADIO_T8) + #define BATTERY_DIVIDER 50000 #else #define BATTERY_DIVIDER 26214 #endif diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 6ffff9df9..6708b6c89 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -27,8 +27,8 @@ #include "board_common.h" #include "hal.h" -#if defined(RADIO_TX12) - #define NAVIGATION_X7_TX12 +#if defined(RADIO_TX12) || defined(RADIO_TX12) + #define NAVIGATION_X7_TX12 #endif #if defined(ROTARY_ENCODER_NAVIGATION) @@ -152,7 +152,7 @@ void extmoduleSendInvertedByte(uint8_t byte); // Trainer driver #define SLAVE_MODE() (g_model.trainerData.mode == TRAINER_MODE_SLAVE) -#if defined(PCBX9D) || (defined(PCBX9DP) && PCBREV < 2019) +#if defined(TRAINER_DETECT_GPIO) // Trainer detect is a switch on the jack #define TRAINER_CONNECTED() (GPIO_ReadInputDataBit(TRAINER_DETECT_GPIO, TRAINER_DETECT_GPIO_PIN) == Bit_RESET) #elif defined(PCBXLITES) @@ -270,6 +270,10 @@ enum EnumKeys KEY_COUNT, KEY_MAX = KEY_COUNT - 1, +#if defined(KEYS_GPIO_REG_BIND) + KEY_BIND, +#endif + #if defined(ROTARY_ENCODER_NAVIGATION) KEY_PLUS, KEY_MINUS, @@ -355,7 +359,7 @@ enum EnumSwitchesPositions SW_SE1, SW_SE2, #endif -#if defined(PCBX9D) || defined(PCBX9DP) || defined(PCBX9E) || defined(PCBX7) || defined(PCBXLITES) || defined(PCBX9LITES) +#if defined(PCBX9D) || defined(PCBX9DP) || defined(PCBX9E) || defined(PCBX7) || defined(PCBXLITES) || defined(PCBX9LITES) || defined(RADIO_T8) SW_SF0, SW_SF1, SW_SF2, @@ -365,7 +369,7 @@ enum EnumSwitchesPositions SW_SG1, SW_SG2, #endif -#if defined(PCBX9D) || defined(PCBX9DP) || defined(PCBX9E) || (defined(PCBX7) && !defined(RADIO_TX12)) +#if defined(PCBX9D) || defined(PCBX9DP) || defined(PCBX9E) || (defined(PCBX7) && !defined(RADIO_TX12)) || defined(RADIO_T8) SW_SH0, SW_SH1, SW_SH2, @@ -442,6 +446,11 @@ enum EnumSwitchesPositions #define STORAGE_NUM_SWITCHES NUM_SWITCHES #define DEFAULT_SWITCH_CONFIG (SWITCH_3POS << 10) + (SWITCH_3POS << 8) + (SWITCH_TOGGLE << 6) + (SWITCH_3POS << 4) + (SWITCH_3POS << 2) + (SWITCH_TOGGLE << 0) #define DEFAULT_POTS_CONFIG (POT_WITH_DETENT << 0) + (POT_WITH_DETENT << 2); +#elif defined(RADIO_T8) + #define NUM_SWITCHES 4 + #define STORAGE_NUM_SWITCHES 8 + #define DEFAULT_SWITCH_CONFIG (SWITCH_2POS << 6) + (SWITCH_3POS << 4) + (SWITCH_3POS << 2) + (SWITCH_2POS << 0); + #define DEFAULT_POTS_CONFIG (0) #elif defined(PCBX7ACCESS) #define NUM_SWITCHES 7 #define STORAGE_NUM_SWITCHES 8 @@ -544,6 +553,11 @@ enum Analogs { #define NUM_SLIDERS 0 #define STORAGE_NUM_POTS 1 #define STORAGE_NUM_SLIDERS 0 +#elif defined(RADIO_T8) + #define NUM_POTS 0 + #define NUM_SLIDERS 0 + #define STORAGE_NUM_POTS 2 + #define STORAGE_NUM_SLIDERS 0 #elif defined(PCBXLITE) || defined(PCBX7) #define NUM_POTS 2 #define NUM_SLIDERS 0 @@ -644,6 +658,11 @@ extern uint16_t adcValues[NUM_ANALOGS]; #define BATTERY_WARN 66 // 6.6V #define BATTERY_MIN 67 // 6.7V #define BATTERY_MAX 83 // 8.3V +#elif defined(RADIO_T8) + // 1S Li-ion / Lipo + #define BATTERY_WARN 33 // 3.3V + #define BATTERY_MIN 32 // 3.2V + #define BATTERY_MAX 42 // 4.2V #else // NI-MH 7.2V #define BATTERY_WARN 65 // 6.5V @@ -710,6 +729,10 @@ uint8_t isBacklightEnabled(); #define USB_NAME "Radiomaster TX12" #define USB_MANUFACTURER 'R', 'M', '_', 'T', 'X', ' ', ' ', ' ' /* 8 bytes */ #define USB_PRODUCT 'R', 'M', ' ', 'T', 'X', '1', '2', ' ' /* 8 Bytes */ +#elif defined(RADIO_T8) + #define USB_NAME "Radiomaster T8" + #define USB_MANUFACTURER 'R', 'M', '_', 'T', 'X', ' ', ' ', ' ' /* 8 bytes */ + #define USB_PRODUCT 'R', 'M', ' ', 'T', '8', ' ', ' ', ' ' /* 8 Bytes */ #else #define USB_NAME "FrSky Taranis" #define USB_MANUFACTURER 'F', 'r', 'S', 'k', 'y', ' ', ' ', ' ' /* 8 bytes */ @@ -864,8 +887,10 @@ void bluetoothDisable(); #endif // USB Charger +#if defined(USB_CHARGER) void usbChargerInit(); bool usbChargerLed(); +#endif // LED driver void ledInit(); @@ -889,7 +914,7 @@ void ledBlue(); #define IS_LCD_RESET_NEEDED() true #define LCD_CONTRAST_MIN 10 #define LCD_CONTRAST_MAX 30 -#if defined(RADIO_TX12) +#if defined(RADIO_TX12) || defined(RADIO_T8) #define LCD_CONTRAST_DEFAULT 21 #else #define LCD_CONTRAST_DEFAULT 15 diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index f70e1e690..0647a236a 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -74,6 +74,25 @@ #define KEYS_GPIO_PIN_MDL GPIO_Pin_11 // PE.11 #define KEYS_GPIO_REG_TELE GPIOD->IDR #define KEYS_GPIO_PIN_TELE GPIO_Pin_2 // PD.02 +#elif defined(RADIO_T8) + #define KEYS_GPIO_REG_PAGEUP GPIOD->IDR + #define KEYS_GPIO_PIN_PAGEUP GPIO_Pin_3 // PD.03 + #define KEYS_GPIO_REG_PAGEDN GPIOD->IDR + #define KEYS_GPIO_PIN_PAGEDN GPIO_Pin_7 // PD.07 + #define KEYS_GPIO_REG_EXIT GPIOB->IDR + #define KEYS_GPIO_PIN_EXIT GPIO_Pin_3 // PB.03 + #define KEYS_GPIO_REG_ENTER GPIOA->IDR + #define KEYS_GPIO_PIN_ENTER GPIO_Pin_13 // PA.13 + #define KEYS_GPIO_REG_SYS GPIOB->IDR + #define KEYS_GPIO_PIN_SYS GPIO_Pin_4 // PB.04 + #define KEYS_GPIO_REG_MDL GPIOE->IDR + #define KEYS_GPIO_PIN_MDL GPIO_Pin_11 // PE.11 + #define KEYS_GPIO_REG_PLUS GPIOE->IDR + #define KEYS_GPIO_PIN_PLUS GPIO_Pin_9 // PE.09 + #define KEYS_GPIO_REG_MINUS GPIOE->IDR + #define KEYS_GPIO_PIN_MINUS GPIO_Pin_10 // PE.10 + #define KEYS_GPIO_REG_BIND GPIOA->IDR + #define KEYS_GPIO_PIN_BIND GPIO_Pin_10 // PA.10 #elif defined(PCBX7) #define KEYS_GPIO_REG_PAGE GPIOD->IDR #define KEYS_GPIO_PIN_PAGE GPIO_Pin_3 // PD.03 @@ -332,6 +351,11 @@ #define HARDWARE_SWITCH_A #define SWITCHES_GPIO_REG_A GPIOC->IDR #define SWITCHES_GPIO_PIN_A GPIO_Pin_13 // PC.13 +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_A + #define HARDWARE_SWITCH_A + #define SWITCHES_GPIO_REG_A GPIOE->IDR + #define SWITCHES_GPIO_PIN_A GPIO_Pin_14 // PE.14 #elif defined(PCBX7) #define STORAGE_SWITCH_A #define HARDWARE_SWITCH_A @@ -362,6 +386,13 @@ #define SWITCHES_GPIO_PIN_B_L GPIO_Pin_6 // PA.06 #define SWITCHES_GPIO_REG_B_H GPIOA->IDR #define SWITCHES_GPIO_PIN_B_H GPIO_Pin_5 // PA.05 +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_B + #define HARDWARE_SWITCH_B + #define SWITCHES_GPIO_REG_B_L GPIOE->IDR + #define SWITCHES_GPIO_PIN_B_L GPIO_Pin_7 // PE.07 + #define SWITCHES_GPIO_REG_B_H GPIOE->IDR + #define SWITCHES_GPIO_PIN_B_H GPIO_Pin_13 // PE.13 #elif defined(PCBX7) #define STORAGE_SWITCH_B #define HARDWARE_SWITCH_B @@ -406,6 +437,13 @@ #define SWITCHES_GPIO_PIN_C_L GPIO_Pin_11 // PD.11 #define SWITCHES_GPIO_REG_C_H GPIOE->IDR #define SWITCHES_GPIO_PIN_C_H GPIO_Pin_0 // PE.00 +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_C + #define HARDWARE_SWITCH_C + #define SWITCHES_GPIO_REG_C_L GPIOE->IDR + #define SWITCHES_GPIO_PIN_C_L GPIO_Pin_2 // PE.02 + #define SWITCHES_GPIO_REG_C_H GPIOE->IDR + #define SWITCHES_GPIO_PIN_C_H GPIO_Pin_1 // PE.01 #elif defined(PCBX7) #define STORAGE_SWITCH_C #define HARDWARE_SWITCH_C @@ -448,6 +486,11 @@ #define SWITCHES_GPIO_PIN_D_L GPIO_Pin_4 // PB.04 #define SWITCHES_GPIO_REG_D_H GPIOB->IDR #define SWITCHES_GPIO_PIN_D_H GPIO_Pin_5 // PB.05 +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_D + #define HARDWARE_SWITCH_D + #define SWITCHES_GPIO_REG_D GPIOD->IDR + #define SWITCHES_GPIO_PIN_D GPIO_Pin_14 // PD.14 #elif defined(RADIO_TX12) #define STORAGE_SWITCH_D #define HARDWARE_SWITCH_D @@ -539,6 +582,9 @@ #define SWITCHES_GPIO_PIN_F_L GPIO_Pin_1 // PE.01 #define SWITCHES_GPIO_REG_F_H GPIOE->IDR #define SWITCHES_GPIO_PIN_F_H GPIO_Pin_2 // PE.02 +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_F + // no SWF #elif defined(PCBX7) #define STORAGE_SWITCH_F #define HARDWARE_SWITCH_F @@ -563,7 +609,7 @@ #define HARDWARE_SWITCH_G #define SWITCHES_GPIO_REG_G GPIOC->IDR #define SWITCHES_GPIO_PIN_G GPIO_Pin_2 // PC.02 -#elif defined(PCBX7) || defined(PCBXLITE) || defined(PCBX9LITE) +#elif defined(PCBX7) || defined(PCBXLITE) || defined(PCBX9LITE) || defined(RADIO_T8) // no SWG #else #define STORAGE_SWITCH_G @@ -586,6 +632,9 @@ #define SWITCHES_GPIO_PIN_H GPIO_Pin_14 // PD.14 #elif defined(PCBXLITE) || defined(PCBX9LITE) // no SWH +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_H + // no SWH #elif defined(RADIO_TX12) #elif defined(PCBX7) #define STORAGE_SWITCH_H @@ -615,6 +664,10 @@ #define SWITCHES_GPIO_REG_I GPIOC->IDR #define SWITCHES_GPIO_PIN_I GPIO_Pin_13 // PC.13 #define STORAGE_SWITCH_J +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_I + #define STORAGE_SWITCH_J + // no SWI/J #elif defined(RADIO_TX12) #define STORAGE_SWITCH_I #define HARDWARE_SWITCH_I @@ -746,6 +799,13 @@ #define KEYS_GPIOC_PINS (GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3|GPIO_Pin_13) #define KEYS_GPIOD_PINS (GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_7 | GPIO_Pin_11 | GPIO_Pin_14 | GPIO_Pin_15) #define KEYS_GPIOE_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8| GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15) +#elif defined(RADIO_T8) + #define KEYS_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE) + #define KEYS_GPIOA_PINS (KEYS_GPIO_PIN_ENTER | KEYS_GPIO_PIN_BIND) + #define KEYS_GPIOB_PINS (KEYS_GPIO_PIN_EXIT | KEYS_GPIO_PIN_SYS) + #define KEYS_GPIOC_PINS (TRIMS_GPIO_PIN_LHR | TRIMS_GPIO_PIN_RVU | TRIMS_GPIO_PIN_RVD) + #define KEYS_GPIOD_PINS (KEYS_GPIO_PIN_PAGEUP | KEYS_GPIO_PIN_PAGEDN | SWITCHES_GPIO_PIN_D | TRIMS_GPIO_PIN_LHL) + #define KEYS_GPIOE_PINS (SWITCHES_GPIO_PIN_C_H | SWITCHES_GPIO_PIN_C_L | TRIMS_GPIO_PIN_RHL | TRIMS_GPIO_PIN_RHR | TRIMS_GPIO_PIN_LVU | TRIMS_GPIO_PIN_LVD | SWITCHES_GPIO_PIN_B_L | KEYS_GPIO_PIN_PLUS | KEYS_GPIO_PIN_MINUS | KEYS_GPIO_PIN_MDL | SWITCHES_GPIO_PIN_B_H | SWITCHES_GPIO_PIN_A) #elif defined(RADIO_X7ACCESS) #define KEYS_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE) #define KEYS_GPIOA_PINS GPIO_Pin_5 @@ -900,6 +960,25 @@ #define ADC_CHANNEL_POT2 ADC_Channel_12 // ADC1_IN12 #define ADC_CHANNEL_BATT ADC_Channel_10 // ADC1_IN10 #define ADC_VREF_PREC2 330 +#elif defined(RADIO_T8) + // No pots + #define ADC_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_DMA2) + #define ADC_RCC_APB1Periph 0 + #define ADC_RCC_APB2Periph RCC_APB2Periph_ADC1 + #define ADC_GPIO_PIN_STICK_RV GPIO_Pin_0 // PA.00 + #define ADC_GPIO_PIN_STICK_RH GPIO_Pin_1 // PA.01 + #define ADC_GPIO_PIN_STICK_LV GPIO_Pin_2 // PA.02 + #define ADC_GPIO_PIN_STICK_LH GPIO_Pin_3 // PA.03 + #define ADC_CHANNEL_STICK_RV ADC_Channel_0 // ADC1_IN0 + #define ADC_CHANNEL_STICK_RH ADC_Channel_1 // ADC1_IN1 + #define ADC_CHANNEL_STICK_LV ADC_Channel_2 // ADC1_IN2 + #define ADC_CHANNEL_STICK_LH ADC_Channel_3 // ADC1_IN3 + #define ADC_GPIO_PIN_BATT GPIO_Pin_0 // PC.00 + #define ADC_GPIOA_PINS (ADC_GPIO_PIN_STICK_RV | ADC_GPIO_PIN_STICK_RH | ADC_GPIO_PIN_STICK_LH | ADC_GPIO_PIN_STICK_LV) + #define ADC_GPIOB_PINS 0 + #define ADC_GPIOC_PINS ADC_GPIO_PIN_BATT + #define ADC_CHANNEL_BATT ADC_Channel_10 + #define ADC_VREF_PREC2 300 #elif defined(PCBX7) #define HARDWARE_POT1 #define HARDWARE_POT2 @@ -1027,6 +1106,16 @@ #define LED_BLUE_GPIO_PIN GPIO_Pin_1 // PB.01 #define LED_RED_GPIO GPIOC #define LED_RED_GPIO_PIN GPIO_Pin_4 // PC.04 +#elif defined(RADIO_T8) + #define STATUS_LEDS + #define GPIO_LED_GPIO_ON GPIO_SetBits + #define GPIO_LED_GPIO_OFF GPIO_ResetBits + #define LED_RED_GPIO GPIOC + #define LED_RED_GPIO_PIN GPIO_Pin_4 // PC.04 + #define LED_BLUE_GPIO GPIOC + #define LED_BLUE_GPIO_PIN GPIO_Pin_5 // PC.05 + #define LED_GREEN_GPIO GPIOB + #define LED_GREEN_GPIO_PIN GPIO_Pin_1 // PB.01 #elif defined(PCBX7) #define STATUS_LEDS #define GPIO_LED_GPIO_ON GPIO_SetBits @@ -1152,7 +1241,7 @@ #define INTMODULE_DMA_STREAM_IRQHandler DMA2_Stream5_IRQHandler #define INTMODULE_DMA_FLAG_TC DMA_IT_TCIF5 #define INTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) -#elif defined(RADIO_T12) || defined(RADIO_TX12) +#elif defined(RADIO_T12) || defined(RADIO_TX12) || defined(RADIO_T8) #define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_DMA1) #define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_USART3 | RCC_APB1Periph_TIM2) #define INTMODULE_RCC_APB2Periph 0 @@ -1401,7 +1490,7 @@ #define TELEMETRY_EXTI_IRQn EXTI9_5_IRQn #define TELEMETRY_EXTI_TRIGGER EXTI_Trigger_Rising -#if defined(RADIO_X7) || defined(RADIO_X7ACCESS) || defined(RADIO_TX12) +#if defined(RADIO_X7) || defined(RADIO_X7ACCESS) || defined(RADIO_TX12) || defined(RADIO_T8) #define TELEMETRY_EXTI_REUSE_INTERRUPT_ROTARY_ENCODER #elif defined(PCBXLITE) || defined(PCBX9LITE) || defined(RADIO_X9DP2019) #define TELEMETRY_EXTI_IRQHandler EXTI9_5_IRQHandler @@ -1477,7 +1566,9 @@ // Heartbeat for iXJT / ISRM synchro #define INTMODULE_HEARTBEAT_TRIGGER EXTI_Trigger_Falling -#if defined(PCBXLITE) +#if !defined(HARDWARE_EXTERNAL_MODULE) + // No heartbeat +#elif defined(PCBXLITE) #define INTMODULE_HEARTBEAT #define INTMODULE_HEARTBEAT_RCC_AHB1Periph RCC_AHB1Periph_GPIOD #define INTMODULE_HEARTBEAT_GPIO GPIOD @@ -1507,7 +1598,7 @@ #define INTMODULE_HEARTBEAT_EXTI_PinSource GPIO_PinSource7 #define INTMODULE_HEARTBEAT_EXTI_LINE EXTI_Line7 #define INTMODULE_HEARTBEAT_EXTI_IRQn EXTI9_5_IRQn -#elif defined(RADIO_X7) || defined(RADIO_TX12) +#elif defined(RADIO_X7) || defined(RADIO_TX12) #define INTMODULE_HEARTBEAT #define INTMODULE_HEARTBEAT_REUSE_INTERRUPT_ROTARY_ENCODER #define INTMODULE_HEARTBEAT_RCC_AHB1Periph RCC_AHB1Periph_GPIOC @@ -1577,15 +1668,17 @@ #define TRAINER_MODULE_SBUS_DMA_STREAM EXTMODULE_USART_RX_DMA_STREAM #define TRAINER_MODULE_SBUS_DMA_CHANNEL EXTMODULE_USART_RX_DMA_CHANNEL #else +#if defined(HARDWARE_EXTERNAL_MODULE) #define TRAINER_MODULE_CPPM #define TRAINER_MODULE_SBUS - #define TRAINER_MODULE_RCC_AHB1Periph RCC_AHB1Periph_GPIOC - #define TRAINER_MODULE_RCC_APB2Periph RCC_APB2Periph_USART6 - #define TRAINER_MODULE_RCC_APB1Periph RCC_APB1Periph_TIM3 - #define TRAINER_MODULE_CPPM_TIMER TRAINER_TIMER - #define TRAINER_MODULE_CPPM_GPIO INTMODULE_HEARTBEAT_GPIO - #define TRAINER_MODULE_CPPM_GPIO_PIN INTMODULE_HEARTBEAT_GPIO_PIN - #define TRAINER_MODULE_CPPM_GPIO_PinSource INTMODULE_HEARTBEAT_EXTI_PinSource +#endif + #define TRAINER_MODULE_RCC_AHB1Periph RCC_AHB1Periph_GPIOC + #define TRAINER_MODULE_RCC_APB2Periph RCC_APB2Periph_USART6 + #define TRAINER_MODULE_RCC_APB1Periph RCC_APB1Periph_TIM3 + #define TRAINER_MODULE_CPPM_TIMER TRAINER_TIMER + #define TRAINER_MODULE_CPPM_GPIO INTMODULE_HEARTBEAT_GPIO + #define TRAINER_MODULE_CPPM_GPIO_PIN INTMODULE_HEARTBEAT_GPIO_PIN + #define TRAINER_MODULE_CPPM_GPIO_PinSource INTMODULE_HEARTBEAT_EXTI_PinSource #define TRAINER_MODULE_CPPM_INTERRUPT_ENABLE TIM_DIER_CC2IE #define TRAINER_MODULE_CPPM_INTERRUPT_FLAG TIM_SR_CC2IF #define TRAINER_MODULE_CPPM_CCMR1 (TIM_CCMR1_IC2F_0 | TIM_CCMR1_IC2F_1 | TIM_CCMR1_CC2S_0) @@ -1665,6 +1758,10 @@ #define BACKLIGHT_CCMR1 TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 // Channel 1, PWM #define BACKLIGHT_CCER TIM_CCER_CC1E #define BACKLIGHT_COUNTER_REGISTER BACKLIGHT_TIMER->CCR1 +#elif defined(RADIO_T8) + #define BACKLIGHT_RCC_AHB1Periph 0 + #define BACKLIGHT_RCC_APB1Periph 0 + #define BACKLIGHT_RCC_APB2Periph 0 #elif defined(PCBX7) #define BACKLIGHT_RCC_AHB1Periph RCC_AHB1Periph_GPIOD #define BACKLIGHT_RCC_APB1Periph RCC_APB1Periph_TIM4 @@ -1692,7 +1789,7 @@ #define KEYS_BACKLIGHT_RCC_AHB1Periph 0 // LCD driver -#if defined(RADIO_TX12) || defined(RADIO_T12) +#if defined(RADIO_TX12) || defined(RADIO_T12) || defined(RADIO_T8) #define LCD_VERTICAL_INVERT #endif #if defined(PCBX9E) @@ -1887,7 +1984,7 @@ #define AUDIO_SPEAKER_ENABLE_GPIO_PIN GPIO_Pin_14 // PD.14 #define HEADPHONE_TRAINER_SWITCH_GPIO GPIOD #define HEADPHONE_TRAINER_SWITCH_GPIO_PIN GPIO_Pin_9 // PD.09 -#elif defined(RADIO_TX12) +#elif defined(RADIO_TX12) || defined(RADIO_T8) #define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA1) #define AUDIO_MUTE_GPIO GPIOE #define AUDIO_MUTE_GPIO_PIN GPIO_Pin_12 // PE.12 @@ -2013,7 +2110,7 @@ #define BT_USART_IRQn USART3_IRQn // #define BT_DMA_Stream_RX DMA1_Stream1 // #define BT_DMA_Channel_RX DMA_Channel_4 -#elif defined(PCBX9D) || defined(PCBX9DP) || defined(RADIO_T12) || defined(RADIO_TX12) +#elif defined(PCBX9D) || defined(PCBX9DP) || defined(RADIO_T12) || defined(RADIO_TX12) | defined(RADIO_T8) #define STORAGE_BLUETOOTH #define BT_RCC_AHB1Periph 0 #define BT_RCC_APB1Periph 0 diff --git a/radio/src/targets/taranis/keys_driver.cpp b/radio/src/targets/taranis/keys_driver.cpp index 6883740c4..d99851441 100644 --- a/radio/src/targets/taranis/keys_driver.cpp +++ b/radio/src/targets/taranis/keys_driver.cpp @@ -88,6 +88,10 @@ uint32_t readKeys() result |= 1 << KEY_SHIFT; #endif +#if defined(KEYS_GPIO_PIN_BIND) + if (~KEYS_GPIO_REG_BIND & KEYS_GPIO_PIN_BIND) + result |= 1 << KEY_BIND; +#endif // if (result != 0) TRACE("readKeys(): result=0x%02x", result); return result; @@ -200,7 +204,8 @@ uint32_t switchState(uint8_t index) uint32_t xxx = 0; switch (index) { -#if defined(RADIO_TX12) + +#if defined(RADIO_TX12) || defined(RADIO_T8) ADD_2POS_CASE(A); ADD_3POS_CASE(B, 1); ADD_3POS_CASE(C, 2); @@ -235,6 +240,8 @@ uint32_t switchState(uint8_t index) ADD_2POS_CASE(D); ADD_3POS_CASE(E, 4); ADD_3POS_CASE(F, 5); +#elif defined(RADIO_T8) + ADD_2POS_CASE(D); #elif defined(PCBX7) ADD_3POS_CASE(D, 3); ADD_2POS_CASE(F); diff --git a/radio/src/targets/taranis/lcd_driver_spi.cpp b/radio/src/targets/taranis/lcd_driver_spi.cpp index e1efa8e15..97f191c2b 100644 --- a/radio/src/targets/taranis/lcd_driver_spi.cpp +++ b/radio/src/targets/taranis/lcd_driver_spi.cpp @@ -20,7 +20,7 @@ #include "opentx.h" -#if defined(RADIO_T12) || defined(RADIO_TX12) +#if defined(RADIO_T12) || defined(RADIO_TX12) || defined(RADIO_T8) #define LCD_CONTRAST_OFFSET -10 #else #define LCD_CONTRAST_OFFSET 160 diff --git a/radio/src/targets/taranis/usb_charger_driver.cpp b/radio/src/targets/taranis/usb_charger_driver.cpp index 7e92c6dbc..78cc3d6a6 100644 --- a/radio/src/targets/taranis/usb_charger_driver.cpp +++ b/radio/src/targets/taranis/usb_charger_driver.cpp @@ -33,5 +33,9 @@ void usbChargerInit() bool usbChargerLed() { +#if defined(SIMU) + return true; +#else return (GPIO_ReadInputDataBit(USB_CHARGER_GPIO, USB_CHARGER_GPIO_PIN) == Bit_RESET && usbPlugged()); +#endif } diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index b10aaf1fc..87f83a9e9 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -294,6 +294,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Mínus" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index f42ba76d8..0d30316e3 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -300,6 +300,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index f28c96b5c..d0dd3e25c 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -300,6 +300,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 5d4a67fad..cbbe3b591 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -297,6 +297,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index b031eda0f..03c1ecf25 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -318,6 +318,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index a5e5d7180..ce6624b67 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -320,6 +320,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Moins" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 06f51beee..eeecb347a 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -321,6 +321,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 6f1063d3d..087305944 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -298,6 +298,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index f87473ef2..4ff16cc53 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -318,6 +318,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" @@ -1130,17 +1133,17 @@ #define TR_ABOUT_MARTIN_2 "Projekty graficzne" #if defined(MANUFACTURER_FRSKY) - #define TR_ABOUT_HARDWARE_1 "FrSky" - #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" - #define TR_ABOUT_HARDWARE_3 "Współautor firmware" -#elif defined(MANUFACTURER_RADIOMASTER) - #define TR_ABOUT_HARDWARE_1 "Radiomaster" - #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" - #define TR_ABOUT_HARDWARE_3 "Współautor firmware" -#elif defined(MANUFACTURER_JUMPER) - #define TR_ABOUT_HARDWARE_1 "JumperRC" - #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" - #define TR_ABOUT_HARDWARE_3 "Współautor firmware" + #define TR_ABOUT_HARDWARE_1 "FrSky" + #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" + #define TR_ABOUT_HARDWARE_3 "Współautor firmware" +#elif defined(MANUFACTURER_RADI OMASTER) + #define TR_ABOUT_HARDWARE_1 "Radiomaster" + #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" + #define TR_ABOUT_HARDWARE_3 "Współautor firmware" +#elif defined(MANUFACTURER_JUMP ER) + #define TR_ABOUT_HARDWARE_1 "JumperRC" + #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" + #define TR_ABOUT_HARDWARE_3 "Współautor firmware" #else #define TR_ABOUT_HARDWARE_1 "Brent Nelson" #define TR_ABOUT_HARDWARE_2 "Sky9x designer/výrobce" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 472a5585e..7ef85235b 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -311,6 +311,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index ad9606ef1..499fcb41d 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -312,6 +312,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/util/build-firmware.py b/radio/util/build-firmware.py index 66d9f053d..b7132d510 100755 --- a/radio/util/build-firmware.py +++ b/radio/util/build-firmware.py @@ -148,6 +148,11 @@ def main(): cmake_options["PCBREV"] = "TX12" firmware_options = options_radiomaster_tx12 maxsize = 65536 * 8 + elif board_name == "t8": + cmake_options["PCB"] = "X7" + cmake_options["PCBREV"] = "T8" + firmware_options = options_radiomaster_t8 + maxsize = 65536 * 8 elif board_name == "t16": cmake_options["PCB"] = "X10" cmake_options["PCBREV"] = "T16" diff --git a/radio/util/fwoptions.py b/radio/util/fwoptions.py index f4438db44..7b2a01edd 100755 --- a/radio/util/fwoptions.py +++ b/radio/util/fwoptions.py @@ -258,6 +258,17 @@ options_radiomaster_tx12 = { "afhds3": ("AFHDS3", "YES", "NO") } +options_radiomaster_t8 = { + "noheli": ("HELI", "NO", "YES"), + "ppmus": ("PPM_UNIT", "US", "PERCENT_PREC1"), + "lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"), + "nogvars": ("GVARS", "NO", "YES"), + "faimode": ("FAI", "YES", None), + "faichoice": ("FAI", "CHOICE", None), + "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), + "bindkey": ("BIND_KEY", "NO", "YES"), +} + options_radiomaster_tx16s = { "noheli": ("HELI", "NO", "YES"), "ppmus": ("PPM_UNIT", "US", "PERCENT_PREC1"), diff --git a/tools/build-radiomaster.py b/tools/build-radiomaster.py index 985bc705a..564463537 100755 --- a/tools/build-radiomaster.py +++ b/tools/build-radiomaster.py @@ -28,6 +28,18 @@ boards = { "PCB": "X7", "PCBREV": "TX12", "DEFAULT_MODE": "2", + }, + "T8_1": { + "PCB": "X7", + "PCBREV": "T8", + "DEFAULT_MODE": "1", + "RADIOMASTER_RTF_RELEASE": "YES", + }, + "T8_2": { + "PCB": "X7", + "PCBREV": "T8", + "DEFAULT_MODE": "2", + "RADIOMASTER_RTF_RELEASE": "YES", } } diff --git a/tools/commit-tests.sh b/tools/commit-tests.sh index af2e7f040..ca25ac809 100755 --- a/tools/commit-tests.sh +++ b/tools/commit-tests.sh @@ -139,6 +139,15 @@ if [[ " TX12 X7 ALL " =~ \ ${FLAVOR}\ ]] ; then make -j"${CORES}" tests-radio fi +if [[ " T8 X7 ALL " =~ \ ${FLAVOR}\ ]] ; then + # OpenTX on T8 + rm -rf ./* + cmake "${COMMON_OPTIONS}" -DPCB=X7 -DPCBREV=TX12 -DHELI=YES -DGVARS=YES "${SRCDIR}" + make -j"${CORES}" ${FIRMARE_TARGET} + make -j"${CORES}" libsimulator + make -j"${CORES}" tests-radio +fi + if [[ " XLITE ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X-Lite rm -rf ./* From e632a335ea63ddb991e143d1d66e80fe80c73e5d Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 15 Jan 2021 17:20:41 +0100 Subject: [PATCH 60/65] Turn off module on model change (#8189) --- radio/src/gui/480x272/model_select.cpp | 1 + radio/src/storage/storage_common.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/radio/src/gui/480x272/model_select.cpp b/radio/src/gui/480x272/model_select.cpp index 661edd814..cd0122ed4 100644 --- a/radio/src/gui/480x272/model_select.cpp +++ b/radio/src/gui/480x272/model_select.cpp @@ -246,6 +246,7 @@ void onModelSelectMenu(const char * result) if (!confirmModelChange()) return; } + // we store the latest changes if any storageFlushCurrentModel(); storageCheck(true); diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index da86cebe8..b21cc007d 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -52,6 +52,9 @@ void preModelLoad() pausePulses(); } pauseMixerCalculations(); +#if defined(EXTMODULE_PWR_GPIO) + EXTERNAL_MODULE_PWR_OFF(); +#endif stopTrainer(); } From 17e272412e2392ea2c0978bc484bd603f282cf31 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 15 Jan 2021 18:01:16 +0100 Subject: [PATCH 61/65] Fixes --- radio/src/storage/storage_common.cpp | 6 +++++- radio/src/targets/taranis/hal.h | 2 +- radio/src/translations/pl.h.txt | 10 +++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index b21cc007d..40bac6a67 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -52,8 +52,12 @@ void preModelLoad() pausePulses(); } pauseMixerCalculations(); + +#if defined(INTMODULE_PWR_GPIO) + INTERNAL_MODULE_OFF(); +#endif #if defined(EXTMODULE_PWR_GPIO) - EXTERNAL_MODULE_PWR_OFF(); + EXTERNAL_MODULE_OFF(); #endif stopTrainer(); diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index 0647a236a..14626fc91 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -1241,7 +1241,7 @@ #define INTMODULE_DMA_STREAM_IRQHandler DMA2_Stream5_IRQHandler #define INTMODULE_DMA_FLAG_TC DMA_IT_TCIF5 #define INTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) -#elif defined(RADIO_T12) || defined(RADIO_TX12) || defined(RADIO_T8) +#elif (defined(RADIO_T12) && defined(HARDWARE_INTERNAL_MODULE))|| defined(RADIO_TX12) || defined(RADIO_T8) #define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_DMA1) #define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_USART3 | RCC_APB1Periph_TIM2) #define INTMODULE_RCC_APB2Periph 0 diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 4ff16cc53..367694cde 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -1136,18 +1136,18 @@ #define TR_ABOUT_HARDWARE_1 "FrSky" #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" #define TR_ABOUT_HARDWARE_3 "Współautor firmware" -#elif defined(MANUFACTURER_RADI OMASTER) +#elif defined(MANUFACTURER_RADIOMASTER) #define TR_ABOUT_HARDWARE_1 "Radiomaster" #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" #define TR_ABOUT_HARDWARE_3 "Współautor firmware" -#elif defined(MANUFACTURER_JUMP ER) +#elif defined(MANUFACTURER_JUMPER) #define TR_ABOUT_HARDWARE_1 "JumperRC" #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" #define TR_ABOUT_HARDWARE_3 "Współautor firmware" #else - #define TR_ABOUT_HARDWARE_1 "Brent Nelson" - #define TR_ABOUT_HARDWARE_2 "Sky9x designer/výrobce" - #define TR_ABOUT_HARDWARE_3 "" + #define TR_ABOUT_HARDWARE_1 "Brent Nelson" + #define TR_ABOUT_HARDWARE_2 "Sky9x designer/výrobce" + #define TR_ABOUT_HARDWARE_3 "" #endif #define TR_ABOUT_PARENTS_1 "Projekty macierzyste" From acb6030d0147e4b05a67bc5d391cc7ebc39d3294 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 15 Jan 2021 18:25:09 +0100 Subject: [PATCH 62/65] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 41ce9cbb0..85246719c 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2097,3 +2097,4 @@ Jens Rainer Hansen Mark Trible Marc Dubois Alan Wilkins +Daniel Hryciuk From c8fa56445de2670ed83ac1cbfdfcbdf2145b79c3 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 15 Jan 2021 19:18:54 +0100 Subject: [PATCH 63/65] Compilation fix --- companion/src/firmwares/sensordata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index 753d2eed0..ebcccfa6a 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -217,7 +217,7 @@ bool SensorData::isSourceAvailable(const ModelData * model, const int index) const int sensorcnt = firmware->getCapability(Sensors); const int i = abs(index); - if (model && sensorcnt > 0 and i <= sensorcnt) { + if (model && sensorcnt > 0 && i <= sensorcnt) { if (i > 0) return model->sensorData[i - 1].isAvailable(); else From a9ad94c99d9bb4c975807ae48201b91ed606bf26 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 15 Jan 2021 19:35:27 +0100 Subject: [PATCH 64/65] Improved T8 RTF defaults (no splash, faster power on) --- radio/src/opentx.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 7a222635a..4e31efacb 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -338,6 +338,8 @@ void generalDefault() // Those settings are for headless radio g_eeGeneral.USBMode = USB_JOYSTICK_MODE; g_eeGeneral.disableRtcWarning = 1; + g_eeGeneral.splashMode = 3; // Disable splash + g_eeGeneral.pwrOnSpeed = 1; // 1 second #endif g_eeGeneral.chkSum = 0xFFFF; From 15a77c268917c8414c3b817c7b4590a95490aeb3 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 15 Jan 2021 19:47:12 +0100 Subject: [PATCH 65/65] T8 swap keys for value edit --- radio/src/gui/navigation/navigation_x7.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/navigation/navigation_x7.cpp b/radio/src/gui/navigation/navigation_x7.cpp index 311a53d9f..7e4255dac 100644 --- a/radio/src/gui/navigation/navigation_x7.cpp +++ b/radio/src/gui/navigation/navigation_x7.cpp @@ -59,7 +59,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(ROTARY_ENCODER_NAVIGATION) if (s_editMode>0 && event==EVT_ROTARY_RIGHT) { #else - if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_MINUS) || event==EVT_KEY_REPT(KEY_MINUS))) { + if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_PLUS) || event==EVT_KEY_REPT(KEY_PLUS))) { #endif newval += min(rotencSpeed, i_max-val); while (isValueAvailable && !isValueAvailable(newval) && newval<=i_max) { @@ -73,7 +73,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(ROTARY_ENCODER_NAVIGATION) else if (s_editMode>0 && event==EVT_ROTARY_LEFT) { #else - if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_PLUS) || event==EVT_KEY_REPT(KEY_PLUS))) { + if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_MINUS) || event==EVT_KEY_REPT(KEY_MINUS))) { #endif newval -= min(rotencSpeed, val-i_min); while (isValueAvailable && !isValueAvailable(newval) && newval>=i_min) {