From 817b82f76f279bccf217145ef0116f16468dc187 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 8 Dec 2020 11:45:43 +0000 Subject: [PATCH 01/40] 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 d10b16d4c69c3099f84e9cdc238dd3b5a690c2ed Mon Sep 17 00:00:00 2001 From: elecpower Date: Sun, 20 Dec 2020 14:14:21 +1100 Subject: [PATCH 02/40] 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 613ed785271c8b0e5cc1ef902bbb578bf441a20a Mon Sep 17 00:00:00 2001 From: elecpower Date: Mon, 21 Dec 2020 23:29:30 +1100 Subject: [PATCH 03/40] 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 2e8e5d9c55f28a99946233ba6cccd6d5a39a7c42 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Dec 2020 08:36:23 +0100 Subject: [PATCH 04/40] 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 05/40] 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 06/40] 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 905886774fd07d78330ec308a68da43460f68b2e Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 8 Jan 2021 19:55:57 +1100 Subject: [PATCH 07/40] 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 08/40] 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 6d2b76a5ed1a77d071896533f0aac2a12e43e99c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 8 Jan 2021 18:25:48 +0100 Subject: [PATCH 09/40] 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 10/40] 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 11/40] 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 12/40] 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 13/40] 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 14/40] 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 15/40] 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 16/40] 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 17/40] 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 18/40] 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 19/40] 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 20/40] 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 21/40] 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 22/40] 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 23/40] 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 24/40] 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 25/40] 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 26/40] 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 27/40] 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 28/40] 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 29/40] 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 30/40] 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 31/40] 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 32/40] 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 zcmV)hK%>8jP)ztl8`_b&|T;P21p>lWYZtOv>6Y40K84x^pA|$On*$zFYv-NFWnDdbicp@ z58`4%NJv6}bR>avbauwr%F30s(#t&}vT7fVjakRBefF-(Opgc;zbyB-#sUD0^*q5n-o!QmZJQT+3_CcS)Oh@9xlM8_Pt?(?0yqUJM6WV!doL>qPseo)*X7 zL1dcspYngjgW)g`$EFSoooMWaVg21frt~|G-(bnm&(GO1-rA;9dTTiv$8q|b_d`$d z-PP5V?f>mx|II$S{ju$LRPdR#^WgBoNw9~bGAt@J9F!Ps+D#XBi-L0ChjQp(Sq25N zMd6&km=(-cN_b>AXcxA}X2rBJKR!P<9)^Enxb($j@D>}k7@=}(#@yzqXS=mBPqZPE z=!X>>$Ah4d2Q-+=y>37z<2Vcx8M2x7fsNxleg`EQmYav+ zsM;&#D7NtXXrye-`xS#`eVD(Wj1c|V;0!}K%Fk*HR*E|6s2s<%tmVE)JC|pzEWFE< ze&RV4ejF#>O!BsJ=!4z4bI11g>}~ksGtZQF-+Olw>VbMPdy+C+v!wwmT^i`Rj-GhB zc6fFai%Jh!*sI}0H&zOLcodu?VX1F62n$SHK`NVsl7cmsBKksjQzqj@ zMu}do;Qqo$I|{&Zolu`1sL1zo8?;@Adyn3AX*c`fJ;)+I!|J+M_M?IgCxf>dSgCs% zTxt6{bSW!eGqr)d9mf^zVb-kT!mttMd|acVMF1CswKmw=w~X$=dymI4Ts*5F7Z(<; z1pA2mS8_^2peV|Z$Aa=4T}6FMBTW#dSFCjevd9Cej2fb<%#plnml`Nr5batw2lvdR zW5aBCblizf%295nwmvzg%c3y@0q~{t_i;++T z*-e&xcdSDp9fWRyktyBpVuSUPr1^O zEDI7i&C@K3X2*MuazQ5{v}oW6XI$HOlcFG#{v731!jSoI%82JL>fi78wg>+Ib)+y?EwpPi z>xu^6Lq@HeMki{MuZK#n$&M8o39NhsS0e^`L*m<*^^HZ_G&0=rR-S{W(3D9|;X%|N z-_K|aEJAl~yn1Lyd{M_>B8VAXUoxI&WA5wK>*Jhk8j!3S%ZW3H>hblfbMtN1zNvg3 z5i@HnOCI9*{Lw*;iD9WZt4Ob+vGn1iwsrr)i!a#Y?>@eQk1F6^3H?2OuzkbAP#{pg z%(M6bQ3X>Y<*$1h5d)RJr|a7qF*O=2rGuZH3#YQus>k!n^IVHp1ifDkX~dswoIQU% zkND%!S~OnKSZ8gHoKB5)Bnv6R3C7wR$n2TO$Z8+9mKw6~?Wv&=-&RG-djCU6_&xjV zv-a@A4~IhLiDnR^hY2-iX+xtTl-3qJH=~YCQD!sb#Mz zzxP01`SrdC`P2kh=ZrI@X(6h}JLxk%hfz~#uP8Ny=8avZI5kvY6~v^)8z$dgPd{qA z@~Y)bBYIK$_4)%3JYf4zKKW#t_cJGGwF)xfYgt;8fr=U!d3JNc?gb#~pntYCSdoXT z^%T3@e$@g?)-HKl7Y*CUb$ zsq}i3T0L1ghX<+)*&(X7bKXiWIvUE`;S;xJAX9`#Q+`3jn4c;T`wTyOVl_OZ074M0xGC!;A)jerKM1E%tSyRrVr0sCURm#}A&FL<8LZ6i zxk=g?-Ja6Z?yS_%oG&#)bcw9?IHRbZOx9XO9az>T`_=|j+1t^ zJlz(9)`MWqF_sz6&5iFC+7X>zK4j0=iod_7c*V`y73o=TTQWyp%IR#&N@6ppyEXpH z%1wLE+5klH+g7^%?@zNVZI?QuMJlzj#rth7ca!$g-%i?F4StU?yNNn$ZP4#}mE4?M zZz<7pCoz9y=_6|rSrUJ5i`F>;(~9vky4u#-YCbKi_2C@}nwu=1Ph91 z`?+4QCKHjO)MDERP5FDG>9?LXNA>%*cvR4Xc(W3%RIP8E|8CKP z+RZ&3*wV%1x#j=W_@7fI z&mY07EH3ufqLL*Yyk)xyM%7{*{jT;*)`!efMDeM)exD;)p^+VIo&68?d-r<5-Zu51 zGv!Gy?OwVjvQk5tR*^9a#-&wyJh#__jJC>p?VhY3nV$6U%|D;jvCkM(gLE}RP*eP@ z5hoD$2G9d(W=mkRK4mD!4?uKZP1srkSQR`s?boWu^RVT8BdFYi1Z(#x|9kk_g5lBk z=NO%>B{TH=t`=ID8;>pFmUZ&k^C1g+BD(R;5LdP^&&M#nkx_hFYp{R5r~V!f)7$PE zoZRw#X(V=fm2FEuGCD0|(Bj#aajVIL|9-1XM2fhDSD#iGdTBj1Z@(2G==EK1*Nl+{)ON;J4c4dEkZgl7X&y8LeY0J^#EyX@7t3cx*8)Tj*w`Mce-!74LYq z$WYJNv}pSFwGeP`(K3KQR`jt~1lO{TM|1HlE9L2Cut}GZX7%$kGPJlq z%J%y@YFkXTUxc-~ainG-{P$5meSa(oUoyz->9p>T+SNP0zMdNG7F`R@0r0%&L55N2 zqy6%V(aV>po*KQaA(SQZ)(UT;<@sL5%#G{7<-HN{NPzeoM}J+-$3d$CW*u$COTFH> z=Cv}R(5L^NvN)K7DI3o{-e}VCpf9z$BN^WNFMMNFS(I_u z5{L@@?yyLhy&y#cnRzR3QL}5==A>vpxi<(IbIk4ER)O^l_WQ+E5j}n1)|QfZzG@wr zlGQwir=`Zbbs$9#?M#U>&l=U8DN}EoYy9`0iwgt1%y{Rf$Rg`g4MF%_ivGFCv3WhA z9McisbI(1C9M%qv(Xwwr3S0+q{D#7R_~D24-h1!a2OoT3fBW0trtf+f?3v&_L}b}~ z+cHns@$SXvx&5?2P{ED9Se2Ek?I)$lLw+f&~2UZc%h^D7& z$pRlwCsN23AF}&evhR(7wlCtX@0SHKTXTT=S;I-sBVLEXfA+JVoetx9S!1^Xi*(y}$q%a~nL9!rEO>%T`T znWxRH`H+MFk>Eyc%{-pJ?g!ZO&fi;tJV~#&kBnP}UTWV(#;=87eDQ|3rc7CTGPG7R z7`{zeJw4BOK4ga8%W9cJ7C2rw;kVy@+dlWX&)JhtK50)s{q$rI;ILaL;3Vd!a1c&_ z_?vIOY45)K?sVVhKmYlO_ znms~TgP)m4v_zOP;jP_QQ@Ds$RDbQ6tQ3c=^tlMu_ae8f{+1zZwLdbp`n^ZP8ZIbO ziOv($zC=h?y|`~PzW?Z>kL)jh`OAa?UVH7e$tXPc+;fw@;$G(GlJ$wqy}J%If{<2aQU1^|Wtdi4DB&ri7k)}gYo?B}QA!^=bQ0UrSG zpLpVliEtD{s8Z<*DHxC72hV{H!vi0KL>3rrfF(nx?!EWk#ULpoGl6U|)`ASazb`u0 z+LuUpB3fGf8SoYekf+=7(EJ?nHKH3C>l!aSKQf+sr7rG03q5qLlJxWS`!;9y_@eY8 zUPnBRLe0pCu+NZtcwQ(UWEaZEY<#W%bZme9>tCnmA?t9s&>b29opJMdCfpC@M_u@V z4*%*`zcQTA4djmd(Iy(EN%=t>N(O}kWI%imi2wNGk5AUb#G^-Et;|z@?65hQ!iJUP zym4}Bye z;G#7ef7S=6kK`Zp8jk#xS6-QP6S)97c$7UoK9UBnzWVB9bzmhPd+f1^h)WgMK#!p8GzN+YP!H=; zql9+gI-Y~`rU5_*kLxf1cy4+>KNN?FJv<1QkBmMZ_KJ_c(Fj5Zu8<;fz;8ly7!%xE zl7$B~!DT_)&<@b`42P9vXrOIi{gHBJFfbcz3oNz&9eKN29lc)He_ulZwR^Q>2quHr zkq6C6gc>4P9-*R^686ycUb@EHjIK3|1r=JMGYXXyvIiLiT}+ANUK767WG9j%#`1Kg zsGs@={em&zbUd6S>xN!3aSun1>zJm85da|-=gQHdb7GL{OBznOdB;v%+_#;2!b3bj=TdthYknG7aJkhQm6nK$V% zfUWj#rQJhUpJ~0|q(-|2r+xc9jaDJ7=Csx2;qqU-gx7UsShu|{Ic!w0gZ>pfAGNvm)JaF3<$sy;eJAW!g2hD zQBVfdN=@0+inD{Xc&roG6m3i}rc?PX5saEZWcOcEdJImh)M(rU{j8iz`);*qi-A0s zHj{6PK)LF5dIQcJ@-iOZT8Bq4>TFpT{wB1i!SQ=-GU|}ZU*T4R??U9KG z%;!a`SZrE4{Ls4a2Mq%Z8RnUTs1e?)WpI_>gDz+?zd$R;L7>9xOhS+M^~}y-W5k1q z<|Q$92@-{^Frs!()|Q^O^}^t-v|DJS7OwQtE-oSGul3T13csgkWeCb2$R?03m6EGs zv;MSD7`;Ad-@0{cad?FHP60Q{98(M&=(hrdM=hB@q6w##>}PbNu`zJ-;SD zG~Umkq9uah6`o$6zjgK?I!v)uB6t`HP*l!U^4hhU_cm8&uTWo7qBh+vq;eBJ+tZhWPsz3M+3J!&b(&EQ29%}75Q3QYx0y6>A>$Aw7IeIW6}!S26@%{mF%7xZO@CWuC2%@;%Tk^UfNs6YAfvsU0oVV)QUGEOmOQ;Jg?-G_h{YB z;8yQ>rh232@eK={L40ONSC_{>-`p4ViaGBQ#IpIyyhcE-pS zV>55D)l=8#)=JGVwAyd(`OIsaLpwtvHQML2SxZJcl8Y@o?zz{ueUN?kgPeO&=6scA z=Y1|rX>v+#S$s3=fa;Ux0J1IEHHYM7xNv#zB^I*C<^@)W#0M7P@Hf{5=rd8{*-l z+8)U4+c!w^k4lNzDK5a#1wn&M-wiJQr{UmgRc7_MXh>szK9eQqefu(WI4OVhaKz}W zc%{d_Vy^DfZG^?e?a>T?DnGNHpb18T35cV(lF37)njrLe_L`OSRZ5jBisGQoI;N}TekX54F;$n4G#32Zesr+2YM+e9?{X_zkn^!8leOd~*MDD=`2Z6K<_ z{vk*}wydXrKI2!!_!_UzQIHbd7!6xgDDDY`sIPAwVx>^twEGJ1sOkViN>Y=q;= zKUI3h)~X9p{ezsxM}_Be{0+V0YC_%9;=|CMvLKc$KK8^S4~@>E^H6*k0G)oLvin$v zitS?;GDnkww0a8pbr%(K@KYWM8H;YXf-|PUk@u*4+XLOpa1;l z$>M3lILEJ^mkV=f-RQNsY>^8W5F^oukQSFWGWO?Os>z%u41hACwOA=)iXNbldGTK8 z+QUcN81rZCwhHq2&$8xGRBF7_XLRKrmd9c<9OTt2rLLLr`|+%`r^XyFgaPK`Gq9R@D8|#T2MI<9j%q`tA|y5Nqm2Sn*brtYb-GRuCn#=C zlo?XSFnU0N%?l>Dmw;P&p{QU2wk2|x9}N+WO_T@l%uvB#l7&l&&7C9Hg7%jrU|OYh zzi*o#=$kYETWR<5ClbyrwEg`)Wc6~_8z#}2K<@td$3IT8&ud)C$4xz|gnkDq+W+UB)droEGZyF4Gd1m*E9Ph4*l| z*hUA*CG4gea%INfIW!vqN759y-Q?2IZazSGl5l$o@KU^JB+*d?D|m*@a9Hwf-$$J^MR)CNo@_0omfkYqZZXOg+XeGTQ#RTHo{_8af-DF7|EV zHOLqedk8-{&U%mx6rWCy)6+BpdZ-4zqk`jaIz`S{QD>+pbcB>i#{NVclzZp{^@=oW z=k~L&6U{6hR;A-K9+j0IU#H&CSf1^Vd&nH32F5c~P+TW{bcFZPs1PaOGb|k~oXysa zIRicDQ#Tf&eF~?txQ@!tK_UFt1fn%OM6E;$8}U36@Y?s0z?NVdsi(bLa|XS%ORxsx z9J(#qSsnVEPMtAZuJFIebThw)Y(eq8$7cx3_gCpT;rhBBVy3lNzeR4JN3A66^J6U6XSgL8t$cWQw!co z@YQk){`*?lqOh0T*D5y!?>+DCR@l($x2`dv4AQekZPjUPC@vL?j)B>C${yv?D=1~1 z%FA$=ZPI&H4`_(=Uh0g7%0vNrpbb95Sm7E^<AAW4k6xa$0E$jDo@au6 zkY&Zp!4U;O`7tYj>(Eo8ioq=_K1uk|QlWAXKt+TV>;OG6;ZTt&51Y{e3;-L6ieX4- zX{pFmNWSnBpQ(Im<@lLOPNgIrrOQEPy*%wVI+u_hLDx^yV3D>?p+rV9O7PV@cn0~; zDa{aJ5M^@hunl`6;?BedK>JwBJ%sI5x;I4IWDN(Wf-$~K!1>HSy*`)e4suwB&FGe) zuo@RtEFcEbWnz#XUBd&)w`TfvR!lwM)J~F_L-1~P8XQ8VBCji> z5z_Su)~P7H%6(HXy{c>}9+(FL77$lLb`lPa7+^$xJBrJXtoEmAih4PGN(Oz|KFb4RJZ_?HULRwby z{A#V3gyS>?tq`-5T&c$c4Ebgf?&RZi3fDXnesE47*9p7nd3COy(2L%fUY)R?u^8t< z(NP_2zEhBKnku--X+wJ6rl_1NVUerD52p-iIE>kT69LqvyA*sL_D7gx^Rh)U zii5$OO)Pww2P-2Z)OxW{`4~PhR-%_5&1v%wrr+udUEr=U5q{ z@EO9sB0C5prB+y_pEcgR31N-$1SOari=NSPa({`v@}-L3L%UT{15x-ZroA~AdZRvFSr1KuwPeIF`nYu077yeR!AsBctf~_ z`*rO-HvtXvd&!6vKnHEo-G5AWXg`=FQ6sc-uly>q1JMGHayR1jUUZQ?yMp5`YkqEf zeVcBIb`RZiXlu-^tMC~2>wOIMAh$&}%oN$LLOJJ577PgI_nA24N;}9sXSP(<3HvF3 z%=QoSOqMz^!gCrOlbHanr=cUG&QOQ=rY-i1`(vm(e(HKr{+%4XMNdx#sn<1u{q(@} zpoGyH7HDEn*ZPyzAkz>g8F)=9>ZS?a>*+>|iL@xXAalrEulUvh$uk4sz3Y|`z^pH} ztr?%?TB1N1?HM;O2L~7 z1AGWjqwkfWr4TivQZrf^?X0a^=+@fW6Q=la(0bqv;jvY@O_uH|tk&5jm3v*~I?5@% zli}E1O$Ljenb#&6<>DU6Q&C2DaxNb~`#9g`xL);7^=p)E!}`J;-M28vboX`e2CY@L z-)1GDf|$hIU?f%yaXmfm2OKK%+$9gz;L8hi-?GgaPPjZY zHEaXu;LW6HEB77f4nLjSjRY|BzFzoxVo)Eq^^}@|^>|ft2BcPROBu8N^&lck3~Kb* z-IG$3_Xuz~FJ8dI82~>`u1JODWS(DRWm+hmM$jF3IzKwd zKKwiKB13kD_t2>D_Z@k$$4<8N5(Nyhl1VMKF8STb@n1wBw5Tc|Mdn#+%3Ui834KvP zt|?FL`Aj)1P|irfdOYSdl45l*T%S$;y`<$jO^G`%$Si^fBp7IlQrLgAV0Gg&d~SJi3> z&+Q*&Py3Y|!NpAoyoWIfR|DcR6MbA`%r(+n9mRYB&*IY+eoRdG={zpT(3oF0Mvy^V zWmx9K3Oh}M6iw&(zoWQ1;!(}}UQ&AKYzyO%&^AyiE!4@Jn>stM%R1|| z*N&SklHq5@_+m&tmhfv3+*UheTOFdTG|$yye+e zsnOP0oryMmVaA>L_$-r<0W!w$v&`#W2DcdJN{;ZN%t8h=Tu?or!_uk8XeK~c15zJV z4;b#M{&*D6zOQ0_UZ9s%=IFj-&p=(~Tim7vh1v?ebv-qI50cc=?@Zz{6Bw@pLfSwA zP<(|@oC78LQ0vaRJacN^A`-f-aMxeI8^s&6OXF~*aTBzAp#CN8-qMaD>r^z+rt|tb z1#W>M8Qo?bEnX+GJjUo=`Z>C{{iv=ftS9n@>}y_)vuSz+IG^7ugLiU}23G}9`DszO zkq7q!Vw-GXj9|a6f+B3PdFqe}4qXL>&M>a0#|JIl*yHz3)B2)=nCe42^>9Tpfe>tv z}6imv6E9!dWEUyJAZ!!S0jbXXhqNXYZ09+{K-6di?%n|gG*fY(zU<I48NuJhG(6_QGY{QW-UoSUjlz5le|167C?{T%2DaeiGmHT1rK5$D){} z{rsM^aXx!b!wFbEuE=7Dg5Qrm`slI|$>xnqTpfk}F=I=lz_kXtz^GS<%^8B8`y&PD zQM8_RwLEb|uZMQ4E=#<;Uh{QkZI0TRwM*BJaOrJigbD94#*k&m$D41yIeB`>5pzfQ zo0)aW9A)rYR`~gS_j*Jfa|U#zoG$m!{>q*AS7a9;^?=ijtP`K1;8@nqaD&MEkw+d` zX25&``{bn^!@O)45AfttT_>-rh1BwxY#iy*SnLMU5H3;A49 zPmgiZSssp~B0X&v6J~F}{q}@>xlUd;gi*#uIrK$C&rvq5nQZS>L~9P64DRd*bFYt6 zei|chRKsJuAoMsC;H|gbnqmo96mEOeGs$!}Q;~xc*myM#MV1_1 zyZ7SJO8QuSlox5}H51QD13=Wmgb40~1;^j$3zT2yJel0&+lIJkh>1lm*4UzmJ@;mw z*TeW;q%l;cBrQP`o`(P48Xh}pM@IV`x?9nVf2K(Cnn;uAj@}Z=G0I+jIvgNG$UKxE za*rRvYK*XJ^TKe$~I+P4T;uAeKHwW{?@5So}Y=fL-tLKiSq8`Fb`TF zJOr2z7=$F6?$>7=LW4lXC6w1=vj|I+@w)0sGx&Gp80|q`WWWVMTr9McBf%Nl>#|=a zM3A7wcjyOaB2@WxE|+M4#)J!xbgg$3!C9tfS4OK4w0x2N4v zAwPEVE(eaSt}?Y*sW`@zX*dbU3mhNhheveth&mqqMZ=;?;Td93wwWLEuwR{)qG9hG z_1zVNYy-ZQW9zxdN6+(T;vM}!TQzr}{M@P6QcH!3o_Xe(iD7^E!yhJVsK*xXWD^dj z!^{ciAm;+I=!PME?n+)*r~}BdlXnT|)CJ)W+Qh6T1{dK93>*>0@caYVr8vg2R0C(t-C#qj`(mb4#FYDlW{m(!D{L)?z z_gSu9dg-Nq(&McMmly$YCL6#a$hsi0%?r$W?v2%?6~dGJXB=i1uFAUnE+3b*@6DY7oj4>FS5A(Ey#w_dzm5KK<(Z=h%*Pla9%cq+v?#xGmGmDQl!cyZt zbRIv1^qv7K9Qqbd8hn`G*>*It|Mfg{29h^6tHi{7Z1^z>9{UA4!(FDh%}yj#y?|2`Mv(9scD zFb6<|roq8Apj;?E{)WuI@WKnrD#9qs&$6817crDp16J?b-~RSQ0F~hP(@#G=#T8%z zHyQ9nom99I%mh%O{bn6bO^kA!F=0P9E%0wW5k;pq0D15mkOvIVcL5BN75CnshKJ0* zaxXZ@1ivP@t?x71*;sg*+6V8Mv$oe_@If-i{`;&iJ+xbNhajHs;X$pPzA(1;ujtN@ z9Yak_d4K)uU!VSl0YE_<%@AOm-&)1QGGlYyfWzDpWy-&pv&iBgA2RADAUZ|Vi}qj7 zugr&W;+wT#tiJJ$Z!D(_`B|V7qxj8le)FIF$)CmRQENsKGLM-r$QY0>lY*Lb zCW0lcd)({hy==2yCL~#imUW#8>DY{xxvd_0h$KAZ9*PfC0>wutKnGdl<)n{Per28? zY$`OY4lp_bg92yE_>s#MiO~w1M^<|~}(T$p&ry`-TU<4Y2%3*zU9`ee)C6|d7 zF23PBB@Zkh#A2iQeUY`zHbXaE>;R=l{eV1R3alQKUw2SNo+T184!)jy=C5z{-X2b8 z_h#O&b$_dVf4{aT^Y)%zPuqN7BN`d~n#c79sdjJHkH}!@bXlv6vIQNA$xSoxwp2XM z8gPbaa2tPg(V6Z&8D+0ObwYR2UdwCZnIoXLhvLHkz^J_V;)|2)^YwRr+i_F*35Qs8 z&Rz?dflgxCAm9+JAeh0`bZXhJ<@H|b0o14{+#ck0!?d2v&XRGck8lUfc=E|7?d6wW zo?5&MLfG#ViAu)LqFJzw6cyzXudw6|L zaYslulKo*u>tVK^H$`wwmms16V8m~J^P4Fl!qtFW2t*63Xu!n!Qkh{@K}YBJl|e90 zNUDGldMp^GG%)kP1c>fCu!^F$5^HLxdOW`J%hRj1Ws6~{J=dEkY|*pe=NWEel3de+5aY!4NibM{<5O!>Z& zm(Os64!76ayge5(kE}l5R;9^x<-N+F=hO84_rL%BqA%F&EzDptUO}cGD*-hZ!1$ao z5gDQHM$?s6T(t9vyge766%K(G7GPlU5g4rl}b0HWwyM|3$Qr`879(Zr$~d&nS! z8G!bDDMyjF3wZ_KB`@V@4R~$v_4#w3-MKRf{QXZw1!HNIF9Xxd+mq+z)cho+kD* zl=kh8_%#ZOfV4zEGB0ckXzu% zDkgEgkL}e0o+F&uVEgB5&qj*O;+4M#uC~hHvfduz)(k*aE>Y4Ne@((c2P%NYI%G9Jnj3%2e%_JSXH7E&yDPir-g_3Pq{|zb zYw*^NmRL7OXj>!euGd;#ArpdH$rQNdmArCf=&PqqmneN^{A=E|)s~E|N1L_Mizale zGx*-qW(!{8>XlCS>2tOVvte*pgz$QfqI9uqAbj0POtaa+74 zxfyBw2_Kd=+b~_dy4oF-e;9VB444(4e@_ojgXNVUoj)21{ETI^a%Z%m3^rUHApV07 zJ~U6IL1D+$MvH|B^xE=@A+A)a#Wf~tvVCbOcQ1_ZRi>7LZ2f*~TmQ4f#EtaeaOs$-wR8ct869fDHm?f|+xPME3d{_~oB@2YRhu+|lZ@nQvbdLoJ*rFZL z(9%=qrPm^Z;8%<4Zc-rIcmDKeyY>DDQ)s*!cFWG$gMJ;H>hupE@t++O+SfN}+@z!j z=L?juHiG1(*~6dz$o5D|y!YO%wYNuwLz%6MO%+X*(*l(P6k$WG?Cg3xK=j)m-=3bE zAig0pdE3V8JIgM-+xOpZPdt7&Sw|1o*vXqyYRRD00G4e^Un`lR@g*}nTZQdhFW+-t z#*654tBxq9wiBVtJ@8h#t#-G(;xr+w(Pf2jxE`)9gu%GEA1Jka$d+xn=n(n~LC&2u_`@8>Q-~H}) z?Wa-H@M?d<}a=J;x;OO&_YjXl45TKrt#cI_$gA!SNA5&%!-WL_c%L-xVtVbk=|htR@BceWa9OPGB|| zyLs^gI)NW3`;UM8<7v*5dy)0@6lF*RD^I-Q?CBW?`OZgejRqjjYxI>Z!l44^>pT~N zuCMh&Zr^SVyLvooFYOG)Wi(s$Mzqff^CIKt>**QWR0ooFN9fkm;I36TOe`Db`vACr zPB)z$5Z~#GW&96A0sYbMb`b*ym!0%VnlaDs&_mJ!$-bZd^rusYbT^eh*wfeFRW$s+ zp^W#XCE`u3ZRbK*TcCejviQ7agnHZ4OM9!5Mn=PX=Qsp9-?-FhMzX$D&a$vp|AwBx zF+w+f_OqW&y23=PUmLKcpP3#HTzRNBwmQ}uo6#cCY9Yo4Y{8aW?$h^EdEP@tb(3ks zAq%qlK5K8({#HL~v8adW>NR_zX9UBoB3L!(fB5PY-udSubTG*2MqV&^d&Z`$&(S!p zL$o?w-j}V}$YjJLmsWVy3OBqA)MO?@mo;=jdOTSOM}wij^J52}9{ORdI5(hcu{mVIOG_tySbb0ULxA%B0VfPHvg9`Kj4=&;5 z=|0&&op|_0>Af#&Z`QA#0nO+|yvvn$?Nu^V&?>#2E^71f*UG!VfV2kmzlViUIPhtfXo6-tN{YjWsBEL%PxqX% z#mISBe-q<0xETmvGDF6>e*DI6z*k>=b$L}753E&#z=LkA(?Dh4Ezdmh#1jP-`i5;8 zTtXu~5Yyx}-Pq{xo$q{SifexHi(gFfKXxo}mmXI$wIJ3OV^k}VrLHlv$3sL0DT`gR zK1ISmTZ7);YMpf{XJPY&`WzuiQ?Z@=oA;iS5=Igpq{t95&Fhf_e%`28C-LvnO7^Nu$t; z|67o8YkXB}e+y0Z*40~DW4R5o_qF#@y1le57^_QS`kv6D=Q~UKulHuB39A9nvoQTM z`Ihz%^JcoezI1~RLdd6eJGQMRVe{eaaiI%<^yVa0L z@ChNZ>k+*g9LdV35f1c}UYNSIWO1og;A$(RxQX`M4caVsgKo{+)vldeM}~Cu-X3Jz zBTK6Mt=+FGPf;5t&+m`bjyhQrcvXHqoY0?KqW4Ea)f10>TBQf(`#xP#Y6zi*K<-w# zTeNRFe2eHuCGzU$hST2UsBq#o?8l^*K^XMx}ttq1(`$!p*O$LDmCa^gK@q@-?u`Ts9Z`r3!!@X(eh}u zXR`YvC2Pg(nNs&Uho1gMCsyg%e)=kKey<_7=RuD#Fecvp+z5%G! r|8DWj^Ag|H3D@G?FVUG{80`N7j4tS^Sk4tg00000NkvXXu0mjfJtkZd 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 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 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 zcmVZ zOVcgaQDxS-M^EcYmNAx$jAgxyB!gs|zz(-ekBFwHO`ACk2m-^TVMYWI3jPu!dO$EE zzz{SM7)eHcV31|K-}mbIRmZARtIj&>%iLApm9eT!O6Pv()VnfwzH{g9-hA`TzU#UG zoSmHozXSNa@B4GV@txbVV>8feXXpAp-b0`7(xpqW-|vTQyWMX1{8xwlU;Wiz4Tu3! zKKS5+34uhOA`n2t;Wv-LZ~VnJ9*2G0u0;1i!tJ;>uZ7>Z4hh6-U3cAeal;Ka44<2B zx+yMSz8oL__{Zbsn{OWW{Z}RE{*_l=nGj2aDwil?-hco73DMk#0RRFh7xNlU6y>mt z_}mamuC2;RL;(6AaJBd+<8=j_q%~^;W$3=9}@{bI-*Oe(;0%xu5&F`1GegJrMJ@ z+isgBs7r?}!X9EtxhG5%QpEDNbK1bpBcgN=;6o8d?ouM^{j%#SS7I=5&?pf}RP2B6 zz4wOCv(G*o&p-eCz?~rE=RWtj`1zmz`Qh9QwoX96{>B?`9Ed=%NYRMjL>iH(qSbX9 z382LBzuX>C64nS_yeTuvoxy+575IsuKOKx;RxrMC9;#^!8*TQvS*wD}c$I;lq zVB~Kci_aTxyb=HOPyZDE{_p=DU;N@1aYIlK=iHqm)+fW-+hjVZh$x<{g~Eq3Ufrv z&aFhS1NAXg1J4CP;dX$CzxHdtHV_pe7=*;%FMs*VgB81FE46#{(MKn&WE|!Zo6N_n z^qgPAAtT6K_>SDj=cJFzZOW;P1aTY0>aYCDuf(ta`mYb-78WdYDGVNn;;q6SMu7W= zPS6aoRqV8ajPP#QI1xRT>p=(}0})AUl;3?|;e(*Sx4!kQAu#auuYWxree}`6;Q5dL z_>U=cw?)|F#WE{V5vUOqMT{RaN8lyFaXmL1H2$m%zkvvNMi3Cb;?=8HCt`ANk~4q! z!ym?9{Ka1kfq}1m?Q6q!7&)K)>}NNrANTLP^Ulcz6`vYunG>Uth|*R_%eg8G!(}>_ z{-}Ond4h}Zfx!az0Ym{IK~T6ExDJSoV?f+1SFXh0{_Wom?gt17fK}0R0PJ_`>GI>Yfi=88elk(_5-36Q53$%<9$qc0&$jtocM?V?}2xI2fTW=jW6hsC=DP=%N5cZj8o{7Ky>%ShZ4?=2K_Zs02|UN~7j-dIL(pi@tc;JD@qY+-yznNYn`2Lpr&^kBc;_TS$H;D~^D1~zmj)RB= zkq~yncFlnXQ)&@|_4@iNuf{_UKRo!BThWcXL$+c!jY^0rim90K*D`V0I;VAw)&Qx_ z)q6YFLTuugU-*Sz7#w^M5MmRe790tV#eQ0_*9}4OF8=W!|6lyg-~7##x!D3-`rPL} z_c%oqh(|qF86}RuC=8tp;Y(eNr9Nk6(S{g30Ek%_I(ScTCEgKwG9*JY4CdXXc=x^c z;<;yk9FIKu$Pmfd1QD;h{>H}<6n_2n*W!)WU!O)WMy_V=-FQ*B2;!@4t^Jv}&HP0N z)#Z>fFm6CtL~8JE5Uu3K!ByE`itG0Kc<%Y<2BLrED__}!NWIi`mma@*^-8??)*JEq z>u6p>gHhJLOOx4k3II->Z3TjkKcCNV3OZ({S5;l&-%02 zU$>9zZ@7MVNt8FhvJ1)fSN>s`2#bO{Tg0lwx9jH2LTwSS8|#Je0TGc?WagiuR=FAP z_;-Kzck$yN|M;2^`_qI=|LYt7`|>(g=Fb^Adl;zdAIj8Hn$ED!V;>Ks(M z%88bV?2H30BHBDqp#zIz6(82I((TYVEAoxVcvOW}Yk*7t6-Q&IW#n87(kro8ac^s2R9vpltM#%Fn>YqOCEruegJ?xk*{cB7 z1Uo$RQ118s`*HmZHw;x_kACsdLBt}`K|R5saTe#~OKVx9BeZ20dHj?g^*giFQcJ|P zo{d}v?!yNI7VnBd%YrcuR-~z51g#l%e5s+J=*>5W7rNulJBRudM*N8|9D`2o= zn=*Hy~4x#wdUCWr>7AHVV5bVWL;{$tsrmxZheb`&j)6%HkayItJ=iQ5MeLQdq! zq;p(x%^ZULoH-8C@2(@~&ei*P@SP4mI1$^S8ySPu@=9EL5z`r!+*UW^^-&799*lu^=HdCJH~vXR}`qx z<<)a*gZ)4>d9MS<^&|Q4y&FF=y=~yu1F>V|+_|t-|D$Efw)1$2c(Be|+%r(P~oY-}I`H`c#I5B8W zu`<43960jBNSbRgcCO{RON}f|imTzXW==e@PHD&-9zG^_!_D)f)!LoYAqu{&vSjDN zbH1+%?PbSi#H;rCw00i9b|RW#W6rzmb6s-KoAb_ohD03{mEXBEsBo>K*dsW8F1Ini zxNc57PNzu^$U;`Jkb5eTrrUMyhZC27-TC(G=>2_afe!#TX0(Jdd-v=;)&?iVmEDY$ zNuP@1r6RDmF>%Col2uS-W*n;LIdYwl?vQ5#qY|eX!TmYbOT;lEbN)g5(PumwI8nTu z>%Iy}YOW&|iQuw^vocPM1i{&U&cndp-lnwaIdc*u)7fixG4?v+`-DJe;|q?a>s-vV zwVkys_>1TVrd^}rj3xE4fZJNvXK6puN=R;_@E7-M=}QhOW@WOLXgqiA)!=)zUi$th z(hh{oBHz>Jp{VvAHh?CQANL9VpvgZ6Zk70QSxDV}Cz^Jr9Ir z5*LI+Z45+gKgLBT_b8^&)O2no5h$uxO~k?1Q&(YfFP3Ew9NhB62MO2je)qcrVo_2L zBC_uc#a<+38U!VzwhL1rp&+g?6Bx6iP&9Vzv>G%88srYzX%%u{zVm8JDOC#=rpQ;V%%6`=V?g zT{PI!W;(Z@E;0?EK_uBTxTIJk>`(o}eo99jo_o}H7a*V_6$pU%M4>LK$uPG82qsa~ zg3O{hE>2rwTU{2ET;N{Nk*F)hbTtrnD!0V{BNe+79Xw*AQS-V^B8pB#wt~`p)R-sC zz|HfPe@AXs*B~R_MG#dTTSbl{;r7m|=UT*~i?mkQ_oF=R4*{AfB7R0OnNYHFHFTdM z$g=}g1g3IhTKCa`l!evYFmhMIb+KNb+o%TC3WqAiAwz!}1D{@gLHf5PhBBSmy2hLn z?$_7ft!HQ*(~25#ZpONaIB{Apxx|a>fJ}EfVf_0=ufGJ5dhIi7KJUjl=T_HWyJ1rX z1^ZFHc9#6DxX$-wdee#8LQZDLxah2ocVk95Ejy<#+PQSe{E{`oE@O&*jGIomwm6NE zR)&mnn;+-B04@S^+?!dv`V3BV*M(hDFzonPGn^|Pp1wDTwuCGBo$b23HMRd%;)$HM!pX3h7>em=h@T?m72}jWovN;j_(YNxfobIX zzWwcQ54w`sg=y?0E=d^YI8mwdAU^RIoiWfb0f0f2MRFAUp8lSju}%-r9p_?j)bCEk zTh`7p3=v^mL(T<9BA+G6 zjj%tFn^NHxBSS_Z+9J_UPE3!U^kX&y+{gxbx?k^+61R^j3^*bE>@e-F6Orr=ek-uB z#1gKg!di&C?~aq4CVHJ%ruM+usIFp?8R!aZRhEHBr$I64s_2GhNfUu~-JvS)tRL(# zZI$WIIu{|z-e=BoT?_ z^y?~);dW1tMVzV<9_NKpYZI^gBW?OqVLIg(Ysh@aY{aXDhfF&S9ag*XTo3_40sLjF zE*L8;GFJV?{jhE!O5cC)y{UncbB}YEKbszxoukR+HNr{p@Kj@yIQp&8=OZ^0xdhbQ z-~;1B*9QiGU|iyX?N{C#iNnZGj2s7|;#hoVFK-q?v7K3KfVojt1u;0GX3eliXGInb z$Cz8}rxsTt9ZJ0&#Tydr?~5<8dDpo<3E+To#~tkGQv}ofq3fIaq>##pUp*7`UTmL>UiH4jjDh( zL7Lg`SzA*ijCJ&9dZT5X@;4g*aS0$AC^)Wa0;q)L^*kWp$>+|Jzvtf5;-)@mBdK;< zor|GA^uggb;y-k#&-^_#RP=m@D$>DLT-xY|R$yi!I&qjUjL{lZ!z61!UlB6%XMLaN z6PE@Kep)ay@h%TQ>r1vmP;TW>bZS>eSeGCM+y`vqF(8tzki*s3_$)XFzqzI`QUrsTAhwHf zSf^Y%h(-ym+~X@9IEgc(vUjLd1A_|L{KhqbC4~=Z;8T0`yvgmlWX{N;g$abv*CUd! z$Oy|g*l!exT=pH+#VMVLtes&4T3pMCvXcd*s|h-8)F@=79WCkeQNVs8js@AK+(t3k zB@%I&5#5gi$A#z>b+}1H)s2-144LP_>-ZWTYW!q_FEduEcUm*-b1}i}#c9q>6@2R@ zdCf`;d*E7+)frRtC9?sM-P6UW63<0>X0=w=&Sna)%CMP`IpT8&d-u{3Y@vUqkRd9J zLn}_u{gs%m#Cca_@3Rhyy3C*7*NN(LG1kVVQ~PL#$yaQY^|iKTQ0bnF1P&^gJE!#y z8JDXB@&*xWgVU*PKHAO>VzrYOn?qFVWn0enl5<)*?OKeD%tdnD*>9lB;{N1CY-b-H zSv>_8kC`u3ae1{bJGLciGZ9uJO{EMs4PeMKErk-v!XC#3CO+H99=b7kbc5M8qD*+WF5@J3L&H)-|{i5!L(MGI1$ea$#cp@hrXx% zrBl(kr2Ann>+^zu-~ayihlY8`{b7a_vJ-Ck`5>tXmTiNGM)ezdxuKimLk~SP^gSgZ zGlF$i{ZDGEu>O{vke>74uax-7D#beI^ej2Impdi^!SRL0+RaqgwM zacY~sP-o?R*=W^Sw-9gO0!(Scq*7fDoofPjUQMv&g2@#6{q-iZb zHvvU2^Mp&k`+fW`?hAbjF$&Sjat|kB zB!eI`*0h9Vt#D3GbfdXCD12J21p&{UC;RlHwVRHt?05Q13HWw$ILrQ7x}ijB88CW` zx>0M_jk~V%Tm|L;V!p%Kv1G#-Arqx@`X&|_3a$Mu9j4-xzqMAhvOqOfs_#*`QF_dp zV0Ti&e|*ZL!{2ptGu&nopAqyY1(kGgS0yvdvhDu-)qAash$70yZi{_LIIPe0R!clp zhKFC{qTmq~C6He8Oq#Rnj46L6_kAa%tlS!Pur<%meL4L)L$omD%vqNo2C{*Wan_Ri zWfCKb^)S}x_)B@l>3WG>cRp@fy{tm!YehonYawLjH&)uATG6N?JkxNE&#szbP^Jn$097|_)^su5r+M6{9}rp9$%%CYh;q98<%Ea z^?CISYk+ezg+tO#VIn9J5m|-9=u47_2!SD&*h^`C764H0;5OGwKChlp6=QjxX&G-wL()c1|2cqr4}ccq0DwU;lML zG84DnWl=ezvI;A4%)mJEiMSW8k2R^La9bx0CD^=v#%iu~@Y*=>bdqU|`Y@vO4;3nc z`Z)+lV=26+pME<2;UE5CASwuXNJ;IdrHzz8r@HnSO&YH}#(IxyATlY8v=<0?|NZw* ztx-o z9|tb%tQ}^I7A-qVF-XAr7}w9*naUAfS-U!*)JOFF{(ftQ{lR%UaNmH0i2}!^_cDFr zTsKEp6|Jx&nem4ONn(I_2nw)^K7$0>J+YTAQ!rlG5WX5b)pJ(cswmFNiFo)j<%XxV zzxv6q%0?-n^6N6_t>U`H4Js-@KoFJd4}-`gE(oZrqgIe{Xf|7zpj^Lp*dHAFZdyXY z-^ZgVVLI=kBel{|VcnsWD2&EXhq6?kp}e8zX<-~KA#4lieykbx$I|^cG3$rI;iQmP zf9ojD2%7nhIw&&aWDyiL8KTw60<1~`Tx77h0pr}c4I;<>!I#X+f{ptAZkKwi=f|4QUA{JfCJ?X5k z4UHLW)77~<$uLCHq_1_KNCsl-xwqjbcHI;#&E~J~8se0&grJ$@O=1vHT8paJ!7+%$ z(B%l}^u&lycK-}2vF_cf)DBoX5qDG18XBjLZ23O-LXN-+p&Tq48pE;BbEw>Q2S0?c z{g~jbqCHE%^=-_E-L=DMmqm%zOfS_hAKe)Ovgio23m_Qdsxj(59E0ozj^RX9tq*Pa ztQqosU*^&{Av5D@&0w>ljsftAn7i{YqQq(G6&G`AEI`M_$c+U?Bo^K`^iP z$q3mpxH6wtujkfmKThCTzjb<=Vmm<3)KsnOE;2$Lu{2u1aarbEmn6b5kr*$POS44$ zEeTHWersP7Bc^rst!vbnJD3it{H<#hH5tb$CuAiPbH+={i1PdB{VH+G-&u3MwZVRP zyeXH*dehVNjB|@3Va@=^#Ge~0)j3gcphCt~nctTU9RDa`)#X_|5f9!)7n;Uk?39p+ zE$5z#!cWWUYjI`F=*GAP)$vil{;(nq!%U=LKP8AWeH9lVLW@KF_uR3q&{+l5V55(z z(k3I=hZ(3pEjYDKh&P8{YaK#dRSBdG&I@R;l zPh4X8ev~lHPwd3DVWCv^l9iQrkr$j3qt)@%^(%t3T$##R_5rZ=uEXIiP^PNrI8e1h zsEeIU3}ui_2T%lRD$4yi9Vot-vl}RSk2%p@NdWDdu>m5AwK>*&?qofQ79y7DHg&K& zYmpfRqJ7(3e^kvvMAFf}IwuRa>3PhWY`{8Y{^HOcGgxz!=r>1}LdA)WNMs+lnyIIe zGENK=gYFW<*exHaD%Rj=<#$UjMnfHR8!1lyRW_n9jw>j2`9eYF`u!o2-tADbk44{Ner41SLjYR*ibEqNIgk2n{I#Y`yZEN}=!9y&7`4xHiNp zCYa)$=zH3-F0le%P9j{F*C*RCd8?4VC8p+A#o8at(O!IT12D37t`6$VNtnH zeLFqR4IK9;b)OzXmjE1(8CPh|2mJ`{tm<1^3tp?vT|4X#lV;8i#KUMrU%tVB`9NBO z*Dgio3MeN6Q99ou%#g{%8KJU8f-WRX*6CadN})AMoq&3*a<3lWf)>H6 z$WO&>y~IuXB8KX&&f2GX$8kc9)STi4pJTIB000}dNkl#N` zx@gUu|0oE^-@FI?Szfd1lAJmGetlH~Yvs<#usM0a{wPxq8Mq*dQWQ>t|G=yBauKuu^kHAfm)AwaO{>-@4p7{WTYBv9Ok<+Lu|E-571bJ$1?U zj3}7}+q&mkeZ~_ao>+5XRpE(#ntORT-S{APi0BOotL1?$Hzj5`Fhqp1mko|XHUl%P z5Qfv)RhgI#;lj+V$ewL&a2j@-5O%C~Ydw66L)hsF;kFlFd~rxaAw6{Gop%n%_GxWS zuu7k0_vM^^9EaX+$YLN6?rTL=QX?~bGGeb4&ZVMy`B$g4&Ap6|bpA%{5)Ju~Rru$B z{^ucYmoV{zMUbvY5=VP ztaw-&g7z`RQwcN{dYfkENZ~fdEkE;_&kWnpD)aCE{_h7Of+LYvq_|_Cy}FkEKHb;F zl`C%z>p~%yh*l{`c|nv?7waHXVpOgXzq7rH_GMiooGj7akLCM^Cq6%FD-ly69E1TP zAy$!!0@42M-~Mgj81yC8xqp?Vm~ovx@8Nu`NOS#=aK7uVyN3JeOky{FvhNOjPfCk5 z!~O&zhw#jh4DQd)6D{KkC$w(Y=6pZ~bQCxQH8v2d)pYGvghYM3!+sF(h9Lz7BZ~L) z!H`{#_h-J66#!c)kRs&6tnW`m=|z>hBd(VUG$07Z4hWhVG7;zXaK@QDo(?-dOy*Wq zd695+HCQU^d2MhZSy9!YRxU<1@>MLWC{?b^EK}EsIr3I&LmKCwwuB8{5QKb7cjF)w%a_-dMdaV!(GfZdd@!nvex~C2Z_Zcfo z_oIY;Z&43Lm~V4XZt!NYL~3OiXn`+}bIxSTOm#(6kFkwrXKlTlQ`w%A4IcDevbe79 z+r(Uq6XLMDZzHOoMK(|_hz|{l7|(}!4hNqEB4H3s!SUL>7FFE%bmPJ#xRy4OW#xD` zw#eL#>-DqKB9P5Co45{}_TirJA-SSB%OqX@x}VGE@>J9sVXs6YCACdvW~BJb5a~c9 zK14eE(6HtO$N)ltY#axdqaRyeVmpszj~n(f#de17=(~YXjFEDX;22z|8)q|dkYUi^ zI9yxnZL;?wr{g%*>8w3+bE*@^Qbo8IXQP!DHd>=7g#dC9>Vm0H{}4zArziq|XdDZt z97rXBP?^R?wyJkSQbxL6v-d39k5M{cP~g7ohcl-#T!i*xsUn^Q*TH?(yd>>WVG+@N zA6Ktl8GJ<_cNu4fAc360k;{=xF-n~TE&@_`Eay_NBQdMv=(~X^YyuzBDwA!aRqvAH z*{zsml5QaRJE*SgW{RlR20M=E>-PIeue|X53&Zrbn?~KEA4f)OE`z%aZm4vYNeF3! zsRSdjxjYfYZ7+*lsx=7?$2};{TLA@s<~i$hjj&h40-W;HlTXIWFTEVMeC(FE>E@dz zE7BJL$qrS{i?u1i+O-tlsOc{~0!n*qMSh^d` z4YI>9pMiwLcIqnk@hHl{xhi(uIPri$Mi`&7ze+z5;u8VsC!c(Bn7wrCEtdzYk!c(v z+9$yHsO4X#e%NdsNQ8L9z+f9_aL8_455FID@YP`(fRkVR^bi{6Z2YqU`JfBAp^GG2c1rFi%1+i}aSm*dvUw@l74f&NvdM+g}0m?H=i zKV-5B5DBA&90cnX1jDf)UWgvVu^>2+O4os$NPzBjT(`e2ZoPatZocuR!M(r>E5hjXX5lP+Czt4cTA1V(8c_HR_ks}~ zp3mdRefSH465+ZSB?!ao>wu$QsX7wZMdn1Midv+ZzZh2bvHpn7Xqi%9Q3p}qP`oL) z)k!Dc5mNbeKt9E*hTdZ2N}kKXL!fBoIv7eUmli4|a4-JWXK6X{nQysP*nfW$Pia? z$Q-n;)kzLfVTrk49g~F){dnrcwZs0AM;@8XarS~avqeArr86Qe5j`!gUJ6SjL+iP+ zNR#`%A-Tp)5b>US?is!?vf<0zML{6X%gu3wi$mY1;39JlTHIfS*Oo|#t<>7=8O;dM z4IY|m=_>`jjw8wkMW(A<&nx^Cb}0)A<(_ zRQJ(LgQ@~d3&8viOy1EVsg~ZL*G0nEq?rP?s3D|`@u1Cipw2VuNB?zm%G z3o*<(T=EQ=@5&4crnO;^z-?l@G`nu53wsOdXZm!lkao|=d@F^_a&rTTVhBCf&?Xa6 zpu6wBd&ppLePFL^nIl*v97VA>l$E^j!ocyE3yFK}Y+Xd2yQH(mCW)x9#2KzexW*rm zH^eL`h&N0(LytF>PI$Gp5+$=-eZSr_#sVqUtzuFuJ`k@VxyCS8B1;fwmhjqPkzwOp zP!ZV?jxL8lAf}sFKO5$V_PS*^j*7lL< zFYPbqdZZugV}L}&WVMu{WzgvLTBlK;#n+)|{5u-Au= zh55J)JgUMtY?x?jan{`EZ$)5uT_&9SvEC=UPYCgSzg~L_S^da5O0`5*wyi?Eb4|wC zbHvKFRf9|=LFE?sK=K}Nem$-0Su;#wx61kp6O~Sc%00;R&fKW3u8|QVb2Y;BcG!Bs z8uHuOI>kQsawm&ZuN_V}u{FvwBv(YM#NW~UOCb}J{%pQ4 zj1Uey=gd~R$t>Zmz&;Wk6NR{0@?vWBF*4)VY7sz*v?Qt$h3Jh${APNyGRU?>#8RAA zx{hH=h$xaneQf9xL_`Lq*7q5ghu9suq??g-vBKvX_`MYAORljRr(&fCvPp5%@+$ z9lJeg9*^gGBu4`ft_MT7lC!9AO$5KOCdLB~JTSHCtW?9C&evuTYleKSs0D;|ai;r7 z3~&NcPndCq50H#FD(7A(ak7EaGG>(c%vqv29~uE7eu@vP*)xfvTvjK{S2ud*2(@jzZ24+{i*@kM1~!wgwMtcJLmb z|NQ5NMu5ncfPj4xQK}?NODC@#_S73%^Wrb&S@;YIPEhJNAQSfhN1~q_xCNZU2|N`6 z!}M$Srq=n3%RIaR=E%Zw6TAqUruQ`;(0rm(~r z4A)5j~Z=M~vuiCYrYyJS>4R-xiLeFjcqT|4aI7?Y4#vJMDVgP_uhEpg+B z+u4HBI#7JFXQr22oOTkI)ht9-Pvh5df>q))Hk41?!{|UE69|b11|j=#(rMO+K))~H zR*E^!K=yMPS>ZyJXXC1C8u!!$GfNA@tYW>V&oI_~3K^l5z+1>A0`}xSloWpZ+ut5I z5(P#u7&1Rgq0(i_fiX5&N6}Pf=7V6+mADU%0kI+4VWf~qY%$i2Q6M#3ylD^!$hrG^ zALYa)uEnMd8@6@EUvMPc065O*W=~UH7gml1^)))0S!K;OCOUnbQ4tIn+>uL~Y!#>Wf4C#2$a#d6;`+H>pfi=UP2u4^8FORSlhzTU2%uw5NwP2U5 z`%8o{OAZ2p>%0S0Z<&u6xtHBHbWkue!iGc)cGK_F>vc2hTe+^)AcYUeRTGge03IA- z3_23uQ5Fv_1R;SS^+w4E14Agxsmn&SiFfXlqjMLS=3aKW21MDdLN z4$6(Xe={xyUydUeoPwwa5D7ve=7}&C#FY}#M>4^!N$$gW?4qdW<34P!NuvfLlg1DT zugPVK(To^jH8qvk_um;;hDe3A!<5C)elXxXIOO<_Fd5>W_uqg25btE`%9gIs?OGEG zaq2BqcH=5AN?aNz>^kM$g0uT&qObC0TVhg)seM$}410eWiYg!$E6D(%2wEdU0AmN) z0WToR2&$YD2E>|q>=!35n10#nqg!QrAb+t-o|UU%vN0Co^rJrVVXzF>?k zwUA1p3j#s{*&r_G0cMVM6!|(ucBV%|IDrxY?gd*y#{zDw_UW}VD|E|{7D{Q%6Uc-w z38KMj#foU0U%^Cd<}`LBk0`DStQ_eoWxDUmbP-J3e6A%d^Lc9VAOu+Wi>r@?S5QS6DJOdGe;LhTq z6zLc^Ofk77iD!a;%WZSMEbqtZ=lF0CWJKId*o|LWqJyzZ79TOmi|O0+CF#Wb+89ch zY;^aH;I|*6*Udz$?vI8J#BcbZcqh)6%+A}2i?M5QN7#$gmxE}8PQ<#1Kn4sRBzoBt z*27-zpzH4t>r%PFr*}c~Sy9yaqSSv*2Fc%1-fg{0%5UoG_;SYynxQ@jxSy62X5tY;Wq-Bch#cVsp zBAx{z|NGzn{;&-}3vi;>wuZPp*_{40a_3$}Wi(0~vJp=o4M?I30$&o=D@HL4AjUx% zA!)@n_1Z4lD~`*!o1&0WBJ6{tzyQN{=yG4rIFkUbZ`TUf+)+x!ug;f8XbxdIh%M+H zGzX$U1=u?h6*(C)E77JO5)!^62>J5MFAtmvq`+}!0@vGS`hap``5)MPU99#)l zpAesj0dsv0Ct#*RV(g8x2_jaaWX?Zp9mkj}SM!GJfudnF=fmGB4w^+x2reP?xIRfj zV<4ech>iO^oEM4y=QVuMv9&`Qv8hB_X5eIm*Wx9NW>|tCC^HgVSv0E@Xh{Hl*398& zAu+g*y>qf)MK%yKKXy}oqC!h25mbz|3=)k7u^@_-ZIOt?%2fy>q@R~rrrhq)A=l}8 zTz1SBpv+>pC0HS?@}HD*RsUobq@Q2;icU;-SGb)~rPhF`h{fygZ0(FS!#Q!9abll@ z%`)UvNF!wZM_XdN60QDR)$><_D|_c`U{))WuMNtl%#4?eb22?viKF@7;GVWFniFvu z$F>G$2+@3CZ7vlxH=-3BDehBdh|Z99UO&}b2n)32v=A?zXI!gFWapIg9t3++( zoD-HW z8X_6)D`Tcbthr})tJN)LCWKK%rxoC9)|E{^aZc|Hucbk8Ai}P3a(r^Vn%>4WE?`wr!n7GUkJ|S!*nx1nKUOVUMok#lkYy8~_0Z$QK5%40&yod$O zcVGqvxyP-*E(%ehBYYxe@!}`jlKpvjEKZ2&_w{=#%tdsTIQsf5I=Kdak$Xgi!lsBA z4Ii#WB!v^iHgs78T?ig}F>!djMNQx(exlxomFt`#jA?vGYHtO06*4M>_Kl?oCJ~z; zu*FfC(|^%mLZuQ~nTT%8RBayrnIQNFG=bQkUQ=GwEuv@n;)pK+AyJ)0G zIhOUYs02Xyhf{f!`A1{rCw(hCFRhqCmj#c7c{pweTinuMoxfNc-I1pg z4^ax@;V-K4e((2wZ-~@v4bq6soEVIfJNl6Y>yVgV*Tq|A==eVOJWH{#lqCC9(BkUWITvw@2yAS`(n+kb2u}Qs-}sIAt>5~s zO&K!ewr&CUp`aN=eC)Bu;+x<6=4PtB zGe}*GOvE`j!9I-fF~{jGB02#xok`0RmMF^mQH73KQV%`!Q2fy!{n4gEY#EgK#QuWy zDt?Vje_pHAw?q$y5u0H&&RT{D1^0n?AmW1$J{W)YXMZ;MgsNz4a<2>tUv|MU3rm%qGu0!CFld{Klig(^6ZiSlgFWZ96A z>(X$c1xg{V|JGYq;+0n*KA#zgh>5Ph|NFlmfB1)gILwv&S%UqgOFKbO z65^6ov%@vnC+_Wc-j27gUX818-d}$``m^`rzkmI)_&R86L#ZQ1rAra!9tk&~)6nvW){TyWe;enWt0PVkFY3vC|z0*h^L= z`Y9GWq>C`=X6z$(Xi5D}y!-Ck14lxt+;h)8@!uYMY{&~f{q)nrG1~f*ZNRt)Ro|OGmzwm`G#Bcxh zZx4%wZIvVUue|coF>ZadQwD^Bj^vi0Mx!AkTr*LgIOvah)r)ctmzoYd5-#H0_oK~w zK7DoU{M1d71T9w(kwIXaP2)*GcJaq|5){+I;WuI`I+`k!=z& zkItMPlojamZF*gg&yt8-34kui7Tt`rk-NiEZdpR$7NVBkM~UHLbS{t{LR1nG zKIh!3J8Oo$)|I%Gnj!I8i9JW)mX7q_`q!U7lK`&Y{ks}PTZEE`E`D0XZRHj#;9WcPbcAI@_3O7pW=52``?b`A?_;L+XHDu>GZ=9w&$zUhh;xXXGmtW11yq3Pp zth~xKY3VROZ|O5uu0_U6L*@Vg002ovPDHLkV1n;*?a}}M 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 33/40] 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 34/40] 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 35/40] 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 36/40] 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 37/40] 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 38/40] 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) { From f5be251d7bd0a558333b0785d3b25f3bf295b6a4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 18 Jan 2021 08:38:57 +0100 Subject: [PATCH 39/40] Update CREDITS.txt --- CREDITS.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 85246719c..8e85ef756 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2098,3 +2098,9 @@ Mark Trible Marc Dubois Alan Wilkins Daniel Hryciuk +Thomas Fox +Sidney B Harden +Augustas Sereika +David Belletete +Karl-Heinz Behn +Lena Hjort From 8cd86e6400b3fa8ed9854d3dde36e49465cd4d3d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 18 Jan 2021 11:15:37 +0100 Subject: [PATCH 40/40] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8e85ef756..1666c5fce 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2104,3 +2104,4 @@ Augustas Sereika David Belletete Karl-Heinz Behn Lena Hjort +Jan Langhorst