From 9a7e1dbca197cc82f12203baf3f2405ea9e8e58b Mon Sep 17 00:00:00 2001 From: philmoz Date: Sat, 1 Jun 2024 15:17:37 +1000 Subject: [PATCH] fix: enforce customisable switch rules when configuring (#4994) --- companion/src/firmwares/modeldata.cpp | 255 ++++++++++++------ companion/src/firmwares/modeldata.h | 22 +- companion/src/modeledit/setup.cpp | 206 ++++++++++---- companion/src/modeledit/setup.h | 111 +++++++- .../src/modeledit/setup_function_switches.ui | 72 ++++- companion/src/multimodelprinter.cpp | 2 +- radio/src/gui/128x64/model_setup.cpp | 169 +++++++++--- radio/src/gui/128x64/radio_diagkeys.cpp | 8 +- radio/src/myeeprom.h | 9 +- radio/src/opentx_constants.h | 4 +- radio/src/switches.cpp | 69 ++++- radio/src/switches.h | 9 + radio/src/translations.cpp | 5 + radio/src/translations.h | 3 + radio/src/translations/cn.h | 2 + radio/src/translations/cz.h | 2 + radio/src/translations/da.h | 2 + radio/src/translations/de.h | 2 + radio/src/translations/en.h | 2 + radio/src/translations/es.h | 2 + radio/src/translations/fi.h | 2 + radio/src/translations/he.h | 2 + radio/src/translations/it.h | 2 + radio/src/translations/jp.h | 2 + radio/src/translations/nl.h | 2 + radio/src/translations/pl.h | 2 + radio/src/translations/pt.h | 2 + radio/src/translations/ru.h | 2 + radio/src/translations/se.h | 2 + radio/src/translations/tw.h | 2 + radio/src/translations/ua.h | 2 + radio/src/translations/untranslated.h | 1 + 32 files changed, 772 insertions(+), 207 deletions(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 3800a988c2a..7bd52b0b716 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -1697,106 +1697,195 @@ AbstractStaticItemModel * ModelData::trainerModeItemModel(const GeneralSettings return mdl; } +unsigned int ModelData::getFuncSwitchConfig(unsigned int index) const +{ + if (index < CPN_MAX_SWITCHES_FUNCTION) + return Helpers::getBitmappedValue(functionSwitchConfig, index, 2); + else + return FUNC_SWITCH_CONFIG_NONE; +} - unsigned int ModelData::getFuncSwitchConfig(unsigned int index) const - { - if (index < CPN_MAX_SWITCHES_FUNCTION) - return Helpers::getBitmappedValue(functionSwitchConfig, index, 2); - else - return FUNC_SWITCH_CONFIG_NONE; - } +void ModelData::setFuncSwitchConfig(unsigned int index, unsigned int value) +{ + if (index < CPN_MAX_SWITCHES_FUNCTION) + Helpers::setBitmappedValue(functionSwitchConfig, value, index, 2); +} - void ModelData::setFuncSwitchConfig(unsigned int index, unsigned int value) - { - if (index < CPN_MAX_SWITCHES_FUNCTION) - Helpers::setBitmappedValue(functionSwitchConfig, value, index, 2); - } +// static +QString ModelData::funcSwitchConfigToString(unsigned int value) +{ + switch (value) { + case FUNC_SWITCH_CONFIG_NONE: + return tr("NONE"); + case FUNC_SWITCH_CONFIG_TOGGLE: + return tr("TOGGLE"); + case FUNC_SWITCH_CONFIG_2POS: + return tr("2POS"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} - // static - QString ModelData::funcSwitchConfigToString(unsigned int value) - { - switch (value) { - case FUNC_SWITCH_CONFIG_NONE: - return tr("NONE"); - case FUNC_SWITCH_CONFIG_TOGGLE: - return tr("TOGGLE"); - case FUNC_SWITCH_CONFIG_2POS: - return tr("2POS"); - default: - return CPN_STR_UNKNOWN_ITEM; - } - } +// static +AbstractStaticItemModel * ModelData::funcSwitchConfigItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_MODELDATA_FUNCSWITCHCONFIG); + for (unsigned int i = FUNC_SWITCH_CONFIG_FIRST; i <= FUNC_SWITCH_CONFIG_LAST; i++) { + mdl->appendToItemList(funcSwitchConfigToString(i), i); + } + mdl->loadItemList(); + return mdl; +} - // static - AbstractStaticItemModel * ModelData::funcSwitchConfigItemModel() - { - AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); - mdl->setName(AIM_MODELDATA_FUNCSWITCHCONFIG); +AbstractStaticItemModel * ModelData::funcSwitchGroupStartSwitchModel(int switchcnt) +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_MODELDATA_FUNCSWITCHGROUPSTARTSWITCH); - for (unsigned int i = FUNC_SWITCH_CONFIG_FIRST; i <= FUNC_SWITCH_CONFIG_LAST; i++) { - mdl->appendToItemList(funcSwitchConfigToString(i), i); - } + mdl->appendToItemList(tr("Restore"), 0); + for (unsigned int i = 1; i <= switchcnt; i += 1) { + mdl->appendToItemList(tr("SW") + QString::number(i), i); + } + mdl->appendToItemList(tr("Off"), switchcnt + 1); - mdl->loadItemList(); - return mdl; - } + mdl->loadItemList(); + return mdl; +} - unsigned int ModelData::getFuncSwitchGroup(unsigned int index) const - { - if (index < CPN_MAX_SWITCHES_FUNCTION) - return Helpers::getBitmappedValue(functionSwitchGroup, index, 2); - else - return 0; - } +AbstractStaticItemModel * ModelData::funcSwitchGroupsModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_MODELDATA_FUNCSWITCHGROUPS); - void ModelData::setFuncSwitchGroup(unsigned int index, unsigned int value) - { - if (index < CPN_MAX_SWITCHES_FUNCTION) - Helpers::setBitmappedValue(functionSwitchGroup, value, index, 2); - } + mdl->appendToItemList(tr("---"), 0); + for (unsigned int i = 1; i <= 3; i += 1) { + mdl->appendToItemList(tr("Group ") + QString::number(i), i); + } - unsigned int ModelData::getFuncSwitchAlwaysOnGroup(unsigned int index) const - { - if (index < CPN_MAX_SWITCHES_FUNCTION) { - unsigned int grp = getFuncSwitchGroup(index); - unsigned int switchcnt = Boards::getCapability(getCurrentFirmware()->getBoard(), Board::FunctionSwitches); - return Helpers::getBitmappedValue(functionSwitchGroup, grp, 1, 2 * switchcnt); - } - else - return 0; - } + mdl->loadItemList(); + return mdl; +} - void ModelData::setFuncSwitchAlwaysOnGroup(unsigned int index, unsigned int value) - { - if (index < CPN_MAX_SWITCHES_FUNCTION) { - unsigned int grp = getFuncSwitchGroup(index); - unsigned int switchcnt = Boards::getCapability(getCurrentFirmware()->getBoard(), Board::FunctionSwitches); - Helpers::setBitmappedValue(functionSwitchGroup, value, grp, 1, 2 * switchcnt); - } - } +unsigned int ModelData::getFuncSwitchGroup(unsigned int index) const +{ + if (index < CPN_MAX_SWITCHES_FUNCTION) + return Helpers::getBitmappedValue(functionSwitchGroup, index, 2); + else + return 0; +} - unsigned int ModelData::getFuncSwitchStart(unsigned int index) const - { - if (index < CPN_MAX_SWITCHES_FUNCTION) - return Helpers::getBitmappedValue(functionSwitchStartConfig, index, 2); - else - return FUNC_SWITCH_START_INACTIVE; - } +void ModelData::setFuncSwitchGroup(unsigned int index, unsigned int value) +{ + if (index < CPN_MAX_SWITCHES_FUNCTION) + Helpers::setBitmappedValue(functionSwitchGroup, value, index, 2); +} - void ModelData::setFuncSwitchStart(unsigned int index, unsigned int value) - { - if (index < CPN_MAX_SWITCHES_FUNCTION) - Helpers::setBitmappedValue(functionSwitchStartConfig, value, index, 2); - } +unsigned int ModelData::getFuncSwitchAlwaysOnGroup(unsigned int group) const +{ + if (group > 0 && group < 4) { + unsigned int switchcnt = Boards::getCapability(getCurrentFirmware()->getBoard(), Board::FunctionSwitches); + return Helpers::getBitmappedValue(functionSwitchGroup, group, 1, 2 * switchcnt); + } + else + return 0; +} + +unsigned int ModelData::getFuncSwitchAlwaysOnGroupForSwitch(unsigned int index) const +{ + if (index < CPN_MAX_SWITCHES_FUNCTION) + return getFuncSwitchAlwaysOnGroup(getFuncSwitchGroup(index)); + else + return 0; +} + +void ModelData::setFuncSwitchAlwaysOnGroup(unsigned int group, unsigned int value) +{ + if (group > 0 && group < 4) { + unsigned int switchcnt = Boards::getCapability(getCurrentFirmware()->getBoard(), Board::FunctionSwitches); + Helpers::setBitmappedValue(functionSwitchGroup, value, group, 1, 2 * switchcnt); + } +} + +unsigned int ModelData::getFuncSwitchStart(unsigned int index) const +{ + if (index < CPN_MAX_SWITCHES_FUNCTION) + return Helpers::getBitmappedValue(functionSwitchStartConfig, index, 2); + else + return FUNC_SWITCH_START_OFF; +} + +void ModelData::setFuncSwitchStart(unsigned int index, unsigned int value) +{ + if (index < CPN_MAX_SWITCHES_FUNCTION) + Helpers::setBitmappedValue(functionSwitchStartConfig, value, index, 2); +} + +int ModelData::getFuncGroupSwitchCount(unsigned int group, int switchcnt) const +{ + int grpSwitchCount = 0; + for (int i = 0; i < switchcnt; i += 1) { + if (getFuncSwitchGroup(i) == group) { + grpSwitchCount += 1; + } + } + return grpSwitchCount; +} + +unsigned int ModelData::getFuncGroupSwitchStart(unsigned int group, int switchcnt) const +{ + bool allDown = true; + for (int i = 0; i < switchcnt; i += 1) { + if (getFuncSwitchGroup(i) == group) { + if (getFuncSwitchStart(i) == FUNC_SWITCH_START_ON) + return i + 1; + if (getFuncSwitchStart(i) != FUNC_SWITCH_START_OFF) + allDown = false; + } + } + if (allDown && getFuncGroupSwitchCount(group, switchcnt) > 0) return switchcnt + 1; + return 0; +} + +void ModelData::setFuncGroupSwitchStart(unsigned int group, unsigned int value, int switchcnt) +{ + for (int i = 0; i < switchcnt; i += 1) { + if (getFuncSwitchGroup(i) == group) + setFuncSwitchStart(i, value ? ModelData::FUNC_SWITCH_START_OFF : ModelData::FUNC_SWITCH_START_PREVIOUS); + } + if (value > 0 && value <= switchcnt) { + setFuncSwitchStart(value - 1, ModelData::FUNC_SWITCH_START_ON); + } +} + +void ModelData::setGroupSwitchState(uint8_t group, int switchcnt) +{ + // Check rules for always on group + // - Toggle switch type not valid, change all switches to 2POS + // - One switch must be turned on, turn on first switch if needed + if (getFuncSwitchAlwaysOnGroup(group)) { + for (int j = 0; j < switchcnt; j += 1) { + if (getFuncSwitchGroup(j) == group) { + setFuncSwitchConfig(j, FUNC_SWITCH_CONFIG_2POS); // Toggle not valid + } + } + if (getFuncGroupSwitchStart(group, switchcnt) == switchcnt + 1) { + // Start state for all switches is off - set all to 'last' + for (int j = 0; j < switchcnt; j += 1) + if (getFuncSwitchGroup(j) == group) + setFuncSwitchStart(j, FUNC_SWITCH_START_PREVIOUS); + } + } +} // static QString ModelData::funcSwitchStartToString(unsigned int value) { switch (value) { - case FUNC_SWITCH_START_INACTIVE: - return tr("Inactive"); - case FUNC_SWITCH_START_ACTIVE: - return tr("Active"); + case FUNC_SWITCH_START_OFF: + return tr("Off"); + case FUNC_SWITCH_START_ON: + return tr("On"); case FUNC_SWITCH_START_PREVIOUS: return tr("Restore"); default: diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index 9288606f9e8..8be1e31c93b 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -46,6 +46,8 @@ class AbstractStaticItemModel; constexpr char AIM_MODELDATA_TRAINERMODE[] {"modeldata.trainermode"}; constexpr char AIM_MODELDATA_FUNCSWITCHCONFIG[] {"modeldata.funcswitchconfig"}; constexpr char AIM_MODELDATA_FUNCSWITCHSTART[] {"modeldata.funcswitchstart"}; +constexpr char AIM_MODELDATA_FUNCSWITCHGROUPSTARTSWITCH[] = {"modeldata.funcswitchgroupstartswitch"}; +constexpr char AIM_MODELDATA_FUNCSWITCHGROUPS[] = {"modeldata.funcswitchgroups"}; class RSSIAlarmData { public: @@ -214,10 +216,9 @@ class ModelData { }; enum FunctionSwitchStart { - FUNC_SWITCH_START_ACTIVE, - FUNC_SWITCH_START_FIRST = FUNC_SWITCH_START_ACTIVE, - FUNC_SWITCH_START_INACTIVE, - //FUNC_SWITCH_START_FIRST = FUNC_SWITCH_START_INACTIVE, + FUNC_SWITCH_START_ON, + FUNC_SWITCH_START_FIRST = FUNC_SWITCH_START_ON, + FUNC_SWITCH_START_OFF, FUNC_SWITCH_START_PREVIOUS, FUNC_SWITCH_START_LAST = FUNC_SWITCH_START_PREVIOUS }; @@ -331,15 +332,22 @@ class ModelData { void setFuncSwitchConfig(unsigned int index, unsigned int value); static QString funcSwitchConfigToString(unsigned int value); static AbstractStaticItemModel * funcSwitchConfigItemModel(); - + static AbstractStaticItemModel * funcSwitchGroupStartSwitchModel(int switchcnt); + static AbstractStaticItemModel * funcSwitchGroupsModel(); + unsigned int getFuncSwitchGroup(unsigned int index) const; void setFuncSwitchGroup(unsigned int index, unsigned int value); - unsigned int getFuncSwitchAlwaysOnGroup(unsigned int index) const; - void setFuncSwitchAlwaysOnGroup(unsigned int index, unsigned int value); + unsigned int getFuncSwitchAlwaysOnGroup(unsigned int group) const; + unsigned int getFuncSwitchAlwaysOnGroupForSwitch(unsigned int index) const; + void setFuncSwitchAlwaysOnGroup(unsigned int group, unsigned int value); + void setGroupSwitchState(uint8_t group, int switchcnt); unsigned int getFuncSwitchStart(unsigned int index) const; void setFuncSwitchStart(unsigned int index, unsigned int value); + int getFuncGroupSwitchCount(unsigned int group, int switchcnt) const; + unsigned int getFuncGroupSwitchStart(unsigned int group, int switchcnt) const; + void setFuncGroupSwitchStart(unsigned int group, unsigned int value, int switchcnt); static QString funcSwitchStartToString(unsigned int value); static AbstractStaticItemModel * funcSwitchStartItemModel(); diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 9de213ce694..24a7bee0922 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -35,7 +35,7 @@ constexpr char FIM_TIMERSWITCH[] {"Timer Switch"}; constexpr char FIM_THRSOURCE[] {"Throttle Source"}; -constexpr char FIM_TRAINERMODE[] {"Trainer Mode"}; +// constexpr char FIM_TRAINERMODE[] {"Trainer Mode"}; constexpr char FIM_ANTENNAMODE[] {"Antenna Mode"}; constexpr char FIM_HATSMODE[] {"Hats Mode"}; @@ -1246,14 +1246,42 @@ void ModulePanel::updateTrainerModeItemModel() } /******************************************************************************/ + +bool FilteredGroupSwitchesModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const +{ + if (sourceRow == 0) return true; + if (sourceRow == m_switchcnt + 1) { + if (m_model->getFuncSwitchAlwaysOnGroup(m_group) || m_model->getFuncGroupSwitchCount(m_group, m_switchcnt) == 0) + return false; + return true; + } + if (m_model->getFuncSwitchGroup(sourceRow - 1) == m_group) + return true; + return false; +} + +bool FilteredSwitchGroupsModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const +{ + if (sourceRow == 0) return true; + if (m_model->getFuncSwitchConfig(m_switch) == ModelData::FUNC_SWITCH_CONFIG_NONE) return false; + return true; +} + +bool FilteredSwitchConfigsModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const +{ + if (sourceRow == ModelData::FUNC_SWITCH_CONFIG_NONE || sourceRow == ModelData::FUNC_SWITCH_CONFIG_2POS) return true; + return !m_model->getFuncSwitchAlwaysOnGroupForSwitch(m_switch); +} + FunctionSwitchesPanel::FunctionSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::FunctionSwitches) { ui->setupUi(this); - AbstractStaticItemModel *fsConfig = ModelData::funcSwitchConfigItemModel(); + fsConfig = ModelData::funcSwitchConfigItemModel(); AbstractStaticItemModel *fsStart = ModelData::funcSwitchStartItemModel(); + AbstractStaticItemModel *fsGroups = ModelData::funcSwitchGroupsModel(); Board::Type board = firmware->getBoard(); @@ -1261,6 +1289,8 @@ FunctionSwitchesPanel::FunctionSwitchesPanel(QWidget * parent, ModelData & model switchcnt = Boards::getCapability(board, Board::FunctionSwitches); + fsGroupStart = ModelData::funcSwitchGroupStartSwitchModel(switchcnt); + for (int i = 0; i < switchcnt; i++) { QLabel * lblSwitchId = new QLabel(this); lblSwitchId->setText(tr("SW%1").arg(i + 1)); @@ -1272,19 +1302,19 @@ FunctionSwitchesPanel::FunctionSwitchesPanel(QWidget * parent, ModelData & model QComboBox * cboConfig = new QComboBox(this); cboConfig->setProperty("index", i); - cboConfig->setModel(fsConfig); + auto configFilter = new FilteredSwitchConfigsModel(fsConfig, i, &model); + cboConfig->setModel(configFilter); + filterSwitchConfigs << configFilter; QComboBox * cboStartPosn = new QComboBox(this); cboStartPosn->setProperty("index", i); cboStartPosn->setModel(fsStart); - QSpinBox * sbGroup = new QSpinBox(this); - sbGroup->setProperty("index", i); - sbGroup->setMaximum(3); - sbGroup->setSpecialValueText("-"); - - QCheckBox * cbAlwaysOnGroup = new QCheckBox(this); - cbAlwaysOnGroup->setProperty("index", i); + QComboBox * cboGroup = new QComboBox(this); + cboGroup->setProperty("index", i); + auto groupFilter = new FilteredSwitchGroupsModel(fsGroups, i, &model); + cboGroup->setModel(groupFilter); + filterSwitchGroups << groupFilter; int row = 0; int coloffset = 1; @@ -1292,20 +1322,43 @@ FunctionSwitchesPanel::FunctionSwitchesPanel(QWidget * parent, ModelData & model ui->gridSwitches->addWidget(aleName, row++, i + coloffset); ui->gridSwitches->addWidget(cboConfig, row++, i + coloffset); ui->gridSwitches->addWidget(cboStartPosn, row++, i + coloffset); - ui->gridSwitches->addWidget(sbGroup, row++, i + coloffset); - ui->gridSwitches->addWidget(cbAlwaysOnGroup, row++, i + coloffset); + ui->gridSwitches->addWidget(cboGroup, row++, i + coloffset); connect(aleName, &AutoLineEdit::currentDataChanged, this, &FunctionSwitchesPanel::on_nameEditingFinished); connect(cboConfig, QOverload::of(&QComboBox::currentIndexChanged), this, &FunctionSwitchesPanel::on_configCurrentIndexChanged); connect(cboStartPosn, QOverload::of(&QComboBox::currentIndexChanged), this, &FunctionSwitchesPanel::on_startPosnCurrentIndexChanged); - connect(sbGroup, QOverload::of(&QSpinBox::valueChanged), this, &FunctionSwitchesPanel::on_groupChanged); - connect(cbAlwaysOnGroup, &QCheckBox::toggled, this, &FunctionSwitchesPanel::on_alwaysOnGroupChanged); + connect(cboGroup, QOverload::of(&QComboBox::currentIndexChanged), this, &FunctionSwitchesPanel::on_groupChanged); aleNames << aleName; cboConfigs << cboConfig; cboStartupPosns << cboStartPosn; - sbGroups << sbGroup; + cboGroups << cboGroup; + } + + for (int i = 0; i < 3; i += 1) { + QLabel * lblGroupId = new QLabel(this); + lblGroupId->setText(tr("Group %1").arg(i + 1)); + + QCheckBox * cbAlwaysOnGroup = new QCheckBox(this); + cbAlwaysOnGroup->setProperty("index", i); + + QComboBox * cboStartPosn = new QComboBox(this); + cboStartPosn->setProperty("index", i); + auto filter = new FilteredGroupSwitchesModel(fsGroupStart, i + 1, &model, switchcnt); + cboStartPosn->setModel(filter); + filterGroupSwitches << filter; + + int row = 0; + int coloffset = 1; + ui->gridGroups->addWidget(lblGroupId, row++, i + coloffset); + ui->gridGroups->addWidget(cbAlwaysOnGroup, row++, i + coloffset); + ui->gridGroups->addWidget(cboStartPosn, row++, i + coloffset); + + connect(cbAlwaysOnGroup, &QCheckBox::toggled, this, &FunctionSwitchesPanel::on_alwaysOnGroupChanged); + connect(cboStartPosn, QOverload::of(&QComboBox::currentIndexChanged), this, &FunctionSwitchesPanel::on_groupStartPosnCurrentIndexChanged); + cbAlwaysOnGroups << cbAlwaysOnGroup; + cboGroupStartupPosns << cboStartPosn; } update(); @@ -1319,38 +1372,31 @@ FunctionSwitchesPanel::~FunctionSwitchesPanel() } void FunctionSwitchesPanel::update() -{ - for (int i = 0; i < switchcnt; i++) { - update(i); - } -} - -void FunctionSwitchesPanel::update(int index) { lock = true; for (int i = 0; i < switchcnt; i++) { + filterSwitchConfigs[i]->invalidate(); + filterSwitchGroups[i]->invalidate(); + aleNames[i]->update(); - cboConfigs[i]->setCurrentIndex(model->getFuncSwitchConfig(i)); + cboConfigs[i]->setCurrentIndex(filterSwitchConfigs[i]->mapFromSource(fsConfig->index(model->getFuncSwitchConfig(i), 0)).row()); cboStartupPosns[i]->setCurrentIndex(model->getFuncSwitchStart(i)); unsigned int grp = model->getFuncSwitchGroup(i); - sbGroups[i]->setValue(grp); - cbAlwaysOnGroups[i]->setChecked(model->getFuncSwitchAlwaysOnGroup(i)); + cboGroups[i]->setCurrentIndex(grp); - if (cboConfigs[i]->currentIndex() < 2) - cboStartupPosns[i]->setEnabled(false); - else - cboStartupPosns[i]->setEnabled(true); + cboStartupPosns[i]->setEnabled(cboConfigs[i]->currentIndex() >= ModelData::FUNC_SWITCH_CONFIG_2POS && (grp == 0)); - if (cboConfigs[i]->currentIndex() < 1) - sbGroups[i]->setEnabled(false); - else - sbGroups[i]->setEnabled(true); + cboGroups[i]->setEnabled(cboConfigs[i]->currentIndex() >= ModelData::FUNC_SWITCH_CONFIG_TOGGLE); + } - if (!(sbGroups[i]->isEnabled()) || grp < 1) - cbAlwaysOnGroups[i]->setEnabled(false); - else - cbAlwaysOnGroups[i]->setEnabled(true); + for (int i = 0; i < 3; i += 1) { + filterGroupSwitches[i]->invalidate(); + + model->setGroupSwitchState(i + 1, switchcnt); + + cbAlwaysOnGroups[i]->setChecked(model->getFuncSwitchAlwaysOnGroup(i + 1)); + cboGroupStartupPosns[i]->setCurrentIndex(filterGroupSwitches[i]->mapFromSource(fsGroupStart->index(model->getFuncGroupSwitchStart(i + 1, switchcnt), 0)).row()); } lock = false; @@ -1372,14 +1418,16 @@ void FunctionSwitchesPanel::on_configCurrentIndexChanged(int index) if (cb && !lock) { lock = true; bool ok = false; - unsigned int i = sender()->property("index").toInt(&ok); - if (ok && model->getFuncSwitchConfig(i) != (unsigned int)index) { - model->setFuncSwitchConfig(i, index); - if (index < 2) - model->setFuncSwitchStart(i, ModelData::FUNC_SWITCH_START_INACTIVE); - if (index < 1) - model->setFuncSwitchGroup(i, 0); - update(i); + unsigned int sw = sender()->property("index").toInt(&ok); + unsigned int config = filterSwitchConfigs[sw]->mapToSource(filterSwitchConfigs[sw]->index(index, 0)).row(); + if (ok && model->getFuncSwitchConfig(sw) != config) { + model->setFuncSwitchConfig(sw, config); + if (config != ModelData::FUNC_SWITCH_CONFIG_2POS) { + model->setFuncSwitchStart(sw, ModelData::FUNC_SWITCH_START_PREVIOUS); + if ((config == ModelData::FUNC_SWITCH_CONFIG_NONE) || model->getFuncSwitchAlwaysOnGroupForSwitch(sw)) + model->setFuncSwitchGroup(sw, 0); + } + update(); emit modified(); emit updateDataModels(); } @@ -1397,9 +1445,31 @@ void FunctionSwitchesPanel::on_startPosnCurrentIndexChanged(int index) if (cb && !lock) { lock = true; bool ok = false; - unsigned int i = sender()->property("index").toInt(&ok); - if (ok && model->getFuncSwitchStart(i) != (unsigned int)index) { - model->setFuncSwitchStart(i, index); + unsigned int sw = sender()->property("index").toInt(&ok); + if (ok && model->getFuncSwitchStart(sw) != (unsigned int)index) { + model->setFuncSwitchStart(sw, index); + update(); + emit modified(); + } + lock = false; + } +} + +void FunctionSwitchesPanel::on_groupStartPosnCurrentIndexChanged(int index) +{ + if (!sender()) + return; + + QComboBox * cb = qobject_cast(sender()); + + if (cb && !lock) { + lock = true; + bool ok = false; + unsigned int grp = sender()->property("index").toInt(&ok); + unsigned int sw = filterGroupSwitches[grp]->mapToSource(filterGroupSwitches[grp]->index(index, 0)).row(); + if (ok && model->getFuncGroupSwitchStart(grp + 1, switchcnt) != sw) { + model->setFuncGroupSwitchStart(grp + 1, sw, switchcnt); + update(); emit modified(); } lock = false; @@ -1411,15 +1481,27 @@ void FunctionSwitchesPanel::on_groupChanged(int value) if (!sender()) return; - QSpinBox * sb = qobject_cast(sender()); + QComboBox * cb = qobject_cast(sender()); - if (sb && !lock) { + if (cb && !lock) { lock = true; bool ok = false; - int i = sender()->property("index").toInt(&ok); - if (ok && model->getFuncSwitchGroup(i) != (unsigned int)value) { - model->setFuncSwitchGroup(i, (unsigned int)value); - update(i); + int sw = sender()->property("index").toInt(&ok); + unsigned int grp = filterSwitchGroups[sw]->mapToSource(filterSwitchGroups[sw]->index(value, 0)).row(); + if (ok && model->getFuncSwitchGroup(sw) != grp) { + unsigned oldGrp = model->getFuncSwitchGroup(sw); + if (model->getFuncSwitchAlwaysOnGroup(grp)) { + if (model->getFuncSwitchConfig(sw) == ModelData::FUNC_SWITCH_CONFIG_TOGGLE) + model->setFuncSwitchConfig(sw, ModelData::FUNC_SWITCH_CONFIG_2POS); + } + if ((grp == 0) || (model->getFuncGroupSwitchStart(grp, switchcnt) == 0)) + model->setFuncSwitchStart(sw, ModelData::FUNC_SWITCH_START_PREVIOUS); + else + model->setFuncSwitchStart(sw, ModelData::FUNC_SWITCH_START_OFF); + model->setFuncSwitchGroup(sw, grp); + if (oldGrp > 0) + model->setFuncGroupSwitchStart(oldGrp, model->getFuncGroupSwitchStart(oldGrp, switchcnt), switchcnt); + update(); emit modified(); } lock = false; @@ -1436,10 +1518,22 @@ void FunctionSwitchesPanel::on_alwaysOnGroupChanged(int value) if (cb && !lock) { lock = true; bool ok = false; - int i = sender()->property("index").toInt(&ok); + int grp = sender()->property("index").toInt(&ok) + 1; if (ok) { - model->setFuncSwitchAlwaysOnGroup(i, (unsigned int)value); + model->setFuncSwitchAlwaysOnGroup(grp, (unsigned int)value); + if (value) { + for (int i = 0; i < switchcnt; i += 1) { + if ((model->getFuncSwitchGroup(i) == grp) && (model->getFuncSwitchConfig(i) == ModelData::FUNC_SWITCH_CONFIG_TOGGLE)) + model->setFuncSwitchConfig(i, ModelData::FUNC_SWITCH_CONFIG_2POS); + } + if (model->getFuncGroupSwitchStart(grp, switchcnt) == switchcnt + 1) { + for (int i = 0; i < switchcnt; i += 1) { + if (model->getFuncSwitchGroup(i) == grp) + model->setFuncSwitchStart(i, ModelData::FUNC_SWITCH_START_PREVIOUS); + } + } + } update(); emit modified(); } diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 0f92d3eb281..c43c905829b 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -25,6 +25,8 @@ #include "compounditemmodels.h" #include "filtereditemmodels.h" +#include + constexpr char MIMETYPE_TIMER[] = "application/x-companion-timer"; namespace Ui { @@ -142,6 +144,103 @@ class ModulePanel : public ModelPanel static bool isExternalModule(int index) { return index > 0; } }; +class FilteredGroupSwitchesModel: public QSortFilterProxyModel +{ + Q_OBJECT + public: + explicit FilteredGroupSwitchesModel(AbstractItemModel * sourceModel, int group, ModelData * model, int switchcnt) : + QSortFilterProxyModel(nullptr), + m_group(group), + m_switchcnt(switchcnt), + m_model(model) + { + setFilterKeyColumn(0); + setDynamicSortFilter(true); + setSourceModel(sourceModel); + } + + explicit FilteredGroupSwitchesModel(AbstractItemModel * sourceModel) : + FilteredGroupSwitchesModel(sourceModel, 0, nullptr, 0) {} + virtual ~FilteredGroupSwitchesModel() {}; + + void setGroup(int group) { m_group = group; } + int getGroup() const { return m_group; } + void setSwitchcnt(int n) { m_switchcnt = n; } + int getSwitchcnt() const { return m_switchcnt; } + void setModel(ModelData* model) { m_model = model; } + ModelData* getModel() const { return m_model; } + + protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const override; + + private: + int m_group = 0; + int m_switchcnt = 0; + ModelData* m_model = nullptr; +}; + +class FilteredSwitchGroupsModel: public QSortFilterProxyModel +{ + Q_OBJECT + public: + explicit FilteredSwitchGroupsModel(AbstractItemModel * sourceModel, int sw, ModelData * model) : + QSortFilterProxyModel(nullptr), + m_switch(sw), + m_model(model) + { + setFilterKeyColumn(0); + setDynamicSortFilter(true); + setSourceModel(sourceModel); + } + + explicit FilteredSwitchGroupsModel(AbstractItemModel * sourceModel) : + FilteredSwitchGroupsModel(sourceModel, 0, nullptr) {} + virtual ~FilteredSwitchGroupsModel() {}; + + void setSwitch(int group) { m_switch = group; } + int getSwitch() const { return m_switch; } + void setModel(ModelData* model) { m_model = model; } + ModelData* getModel() const { return m_model; } + + protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const override; + + private: + int m_switch = 0; + ModelData* m_model = nullptr; +}; + +class FilteredSwitchConfigsModel: public QSortFilterProxyModel +{ + Q_OBJECT + public: + explicit FilteredSwitchConfigsModel(AbstractItemModel * sourceModel, int sw, ModelData * model) : + QSortFilterProxyModel(nullptr), + m_switch(sw), + m_model(model) + { + setFilterKeyColumn(0); + setDynamicSortFilter(true); + setSourceModel(sourceModel); + } + + explicit FilteredSwitchConfigsModel(AbstractItemModel * sourceModel) : + FilteredSwitchConfigsModel(sourceModel, 0, nullptr) {} + virtual ~FilteredSwitchConfigsModel() {}; + + void setSwitch(int group) { m_switch = group; } + int getSwitch() const { return m_switch; } + void setModel(ModelData* model) { m_model = model; } + ModelData* getModel() const { return m_model; } + + protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const override; + + private: + int m_switch = 0; + ModelData* m_model = nullptr; +}; + class FunctionSwitchesPanel : public ModelPanel { Q_OBJECT @@ -150,8 +249,7 @@ class FunctionSwitchesPanel : public ModelPanel FunctionSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware); virtual ~FunctionSwitchesPanel(); - virtual void update(); - void update(int index); + void update(); signals: void updateDataModels(); @@ -160,6 +258,7 @@ class FunctionSwitchesPanel : public ModelPanel void on_nameEditingFinished(); void on_configCurrentIndexChanged(int index); void on_startPosnCurrentIndexChanged(int index); + void on_groupStartPosnCurrentIndexChanged(int index); void on_groupChanged(int value); void on_alwaysOnGroupChanged(int value); @@ -168,9 +267,15 @@ class FunctionSwitchesPanel : public ModelPanel QVector aleNames; QVector cboConfigs; QVector cboStartupPosns; - QVector sbGroups; + QVector cboGroups; QVector cbAlwaysOnGroups; + QVector cboGroupStartupPosns; + QVector filterGroupSwitches; + QVector filterSwitchGroups; + QVector filterSwitchConfigs; int switchcnt; + AbstractStaticItemModel *fsConfig; + AbstractStaticItemModel *fsGroupStart; }; class SetupPanel : public ModelPanel diff --git a/companion/src/modeledit/setup_function_switches.ui b/companion/src/modeledit/setup_function_switches.ui index d5c78893854..3529dd0a435 100644 --- a/companion/src/modeledit/setup_function_switches.ui +++ b/companion/src/modeledit/setup_function_switches.ui @@ -63,17 +63,17 @@ - - + + - Type + Name - - + + - Name + Type @@ -97,17 +97,71 @@ - - + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 75 + true + + + + Switch Groups + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + QLayout::SetMinimumSize + + + + + + + + + + Always On + + + + Start + + + - + Qt::Horizontal diff --git a/companion/src/multimodelprinter.cpp b/companion/src/multimodelprinter.cpp index 0dc55993b0a..d46a93641e4 100644 --- a/companion/src/multimodelprinter.cpp +++ b/companion/src/multimodelprinter.cpp @@ -1002,7 +1002,7 @@ QString MultiModelPrinter::printFunctionSwitches() columns.appendRowStart(tr("Always On"), 20); for (int i = 0; i < numFS; i++) { - COMPARECELLWIDTH((model->getFuncSwitchAlwaysOnGroup((unsigned int)i) == 0 ? "No" : "Yes"), colwidth); + COMPARECELLWIDTH((model->getFuncSwitchAlwaysOnGroupForSwitch((unsigned int)i) == 0 ? "No" : "Yes"), colwidth); } columns.appendRowEnd(); diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index d9d3303ef95..fb8e3cb4899 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -80,7 +80,15 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_SW4, ITEM_MODEL_SETUP_SW5, ITEM_MODEL_SETUP_SW6, - ITEM_MODEL_SETUP_FS_STARTUP, + ITEM_MODEL_SETUP_GROUP1_LABEL, + ITEM_MODEL_SETUP_GROUP1_ALWAYS_ON, + ITEM_MODEL_SETUP_GROUP1_START, + ITEM_MODEL_SETUP_GROUP2_LABEL, + ITEM_MODEL_SETUP_GROUP2_ALWAYS_ON, + ITEM_MODEL_SETUP_GROUP2_START, + ITEM_MODEL_SETUP_GROUP3_LABEL, + ITEM_MODEL_SETUP_GROUP3_ALWAYS_ON, + ITEM_MODEL_SETUP_GROUP3_START, #endif ITEM_MODEL_SETUP_EXTENDED_LIMITS, ITEM_MODEL_SETUP_EXTENDED_TRIMS, @@ -248,6 +256,7 @@ uint8_t THROTTLE_ROW(uint8_t value) return HIDDEN_ROW; } +#if defined(FUNCTION_SWITCHES) uint8_t FS_ROW(uint8_t value) { if (expandState.functionSwitches) @@ -255,6 +264,11 @@ uint8_t FS_ROW(uint8_t value) return HIDDEN_ROW; } +uint8_t G1_ROW(int8_t value) { return (firstSwitchInGroup(1) >= 0) ? value : HIDDEN_ROW; } +uint8_t G2_ROW(int8_t value) { return (firstSwitchInGroup(2) >= 0) ? value : HIDDEN_ROW; } +uint8_t G3_ROW(int8_t value) { return (firstSwitchInGroup(3) >= 0) ? value : HIDDEN_ROW; } +#endif + uint8_t VIEWOPT_ROW(uint8_t value) { if (expandState.viewOpt) @@ -356,7 +370,15 @@ inline uint8_t TIMER_ROW(uint8_t timer, uint8_t value) FS_ROW(NAVIGATION_LINE_BY_LINE|3), \ FS_ROW(NAVIGATION_LINE_BY_LINE|3), \ FS_ROW(NAVIGATION_LINE_BY_LINE|3), \ - FS_ROW(NAVIGATION_LINE_BY_LINE|(NUM_FUNCTIONS_SWITCHES-1)), + FS_ROW(G1_ROW(LABEL())), \ + FS_ROW(G1_ROW(0)), \ + FS_ROW(G1_ROW(0)), \ + FS_ROW(G2_ROW(LABEL())), \ + FS_ROW(G2_ROW(0)), \ + FS_ROW(G2_ROW(0)), \ + FS_ROW(G3_ROW(LABEL())), \ + FS_ROW(G3_ROW(0)), \ + FS_ROW(G3_ROW(0)), #else #define FUNCTION_SWITCHES_ROWS #endif @@ -544,7 +566,7 @@ inline uint8_t USB_JOYSTICK_APPLYROW() #endif #if defined(FUNCTION_SWITCHES) -static const char* _fct_sw_start[] = { STR_CHAR_UP, STR_CHAR_DOWN, "=" }; +static const char* _fct_sw_start[] = { STR_CHAR_DOWN, STR_CHAR_UP, "=" }; #endif uint8_t viewOptChoice(coord_t y, const char* title, uint8_t value, uint8_t attr, event_t event) @@ -553,6 +575,31 @@ uint8_t viewOptChoice(coord_t y, const char* title, uint8_t value, uint8_t attr, return editChoice(96, y, nullptr, STR_ADCFILTERVALUES, value, 0, 2, attr, event); } +#if defined(FUNCTION_SWITCHES) +int cfsIndex; +uint8_t cfsGroup; + +bool checkCFSTypeAvailable(int val) +{ + int group = FSWITCH_GROUP(cfsIndex); + if (group > 0 && IS_FSWITCH_GROUP_ON(group) && val == SWITCH_TOGGLE) + return false; + return true; +} + +bool checkCFSGroupAvailable(int group) +{ + if (FSWITCH_CONFIG(cfsIndex) == SWITCH_TOGGLE && group && IS_FSWITCH_GROUP_ON(group)) + return false; + return true; +} + +bool checkCFSSwitchAvailable(int sw) +{ + return (sw == 0) || (sw == NUM_FUNCTIONS_SWITCHES + 1) || (FSWITCH_GROUP(sw - 1) == cfsGroup); +} +#endif + void menuModelSetup(event_t event) { int8_t old_editMode = s_editMode; @@ -781,8 +828,8 @@ void menuModelSetup(event_t event) timer->persistent = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_PERSISTENT, STR_VPERSISTENT, timer->persistent, 0, 2, attr, event, INDENT_WIDTH); break; } -#if defined(FUNCTION_SWITCHES) +#if defined(FUNCTION_SWITCHES) case ITEM_MODEL_SETUP_LABEL: expandState.functionSwitches = expandableSection(y, STR_FUNCTION_SWITCHES, expandState.functionSwitches, attr, event); break; @@ -795,61 +842,107 @@ void menuModelSetup(event_t event) case ITEM_MODEL_SETUP_SW6: { int index = k - ITEM_MODEL_SETUP_SW1; - int config = FSWITCH_CONFIG(index); lcdDrawSizedText(INDENT_WIDTH, y, STR_CHAR_SWITCH, 2, menuHorizontalPosition < 0 ? attr : 0); lcdDrawText(lcdNextPos, y, switchGetName(index+switchGetMaxSwitches()), menuHorizontalPosition < 0 ? attr : 0); + if (ZEXIST(g_model.switchNames[index]) || (attr && s_editMode > 0 && menuHorizontalPosition == 0)) editName(35, y, g_model.switchNames[index], LEN_SWITCH_NAME, event, menuHorizontalPosition == 0 ? attr : 0, 0, old_editMode); else lcdDrawMMM(35, y, menuHorizontalPosition == 0 ? attr : 0); - config = editChoice(30 + 5*FW, y, "", STR_SWTYPES, config, SWITCH_NONE, SWITCH_2POS, menuHorizontalPosition == 1 ? attr : 0, event); - if (attr && checkIncDec_Ret) { - swconfig_t mask = (swconfig_t)0x03 << (2*index); - g_model.functionSwitchConfig = (g_model.functionSwitchConfig & ~mask) | ((swconfig_t(config) & 0x03) << (2*index)); - } - config = FSWITCH_GROUP(index); - config = editChoice(30 + 13 * FW, y, "", STR_FSGROUPS, config, 0, 3, menuHorizontalPosition == 2 ? attr : 0, event); - if (attr && checkIncDec_Ret && menuHorizontalPosition == 2) { - swconfig_t mask = (swconfig_t) 0x03 << (2 * index); - g_model.functionSwitchGroup = (g_model.functionSwitchGroup & ~mask) | ((swconfig_t(config) & 0x03) << (2 * index)); + cfsIndex = index; + int config = FSWITCH_CONFIG(index); + config = editChoice(30 + 5*FW, y, "", STR_SWTYPES, config, SWITCH_NONE, SWITCH_2POS, menuHorizontalPosition == 1 ? attr : 0, event, 0, checkCFSTypeAvailable); + if (attr && checkIncDec_Ret && menuHorizontalPosition == 1) { + FSWITCH_SET_CONFIG(index, config); + if (config == SWITCH_TOGGLE) { + FSWITCH_SET_STARTUP(index, FS_START_PREVIOUS); // Toggle switches do not have startup position + } } - if (FSWITCH_GROUP(index)) { - uint8_t groupeAlwaysOn = IS_FSWITCH_GROUP_ON(config); - groupeAlwaysOn = editCheckBox(groupeAlwaysOn, 30 + 15 * FW, y, "", menuHorizontalPosition == 3 ? attr : 0, event); - if (attr && checkIncDec_Ret && menuHorizontalPosition == 3) { - swconfig_t mask = (swconfig_t) 0x01 << (2 * NUM_FUNCTIONS_SWITCHES + config); - g_model.functionSwitchGroup = (g_model.functionSwitchGroup & ~mask) | (groupeAlwaysOn << (2 * NUM_FUNCTIONS_SWITCHES + config)); + if (config != SWITCH_NONE) { + uint8_t group = FSWITCH_GROUP(index); + group = editChoice(30 + 13 * FW, y, "", STR_FSGROUPS, group, 0, 3, menuHorizontalPosition == 2 ? attr : 0, event, 0, checkCFSGroupAvailable); + if (attr && checkIncDec_Ret && menuHorizontalPosition == 2) { + int oldGroup = FSWITCH_GROUP(index); + if (groupHasSwitchOn(group)) + setFSLogicalState(index, 0); + FSWITCH_SET_GROUP(index, group); + if (group > 0) { + FSWITCH_SET_STARTUP(index, groupDefaultSwitch(group) == -1 ? FS_START_PREVIOUS : FS_START_OFF); + if (config == SWITCH_TOGGLE && IS_FSWITCH_GROUP_ON(group)) + FSWITCH_SET_CONFIG(index, SWITCH_2POS); + setGroupSwitchState(group, index); + } else { + FSWITCH_SET_STARTUP(index, FS_START_PREVIOUS); + } + setGroupSwitchState(oldGroup); } - } - else if (attr && menuHorizontalPosition == 3) { // Non visible checkbox + + if (config != SWITCH_TOGGLE && group == 0) { + int startPos = FSWITCH_STARTUP(index); + lcdDrawText(30 + 15 * FW, y, _fct_sw_start[startPos], attr && (menuHorizontalPosition == 3) ? (s_editMode ? INVERS + BLINK : INVERS) : 0); + if (attr && menuHorizontalPosition == 3) { + startPos = checkIncDec(event, startPos, FS_START_ON, FS_START_PREVIOUS, EE_MODEL); + FSWITCH_SET_STARTUP(index, startPos); + } + } else if (attr && menuHorizontalPosition == 3) { + repeatLastCursorMove(event); + } + } else if (attr && menuHorizontalPosition >= 2) { repeatLastCursorMove(event); } break; } - case ITEM_MODEL_SETUP_FS_STARTUP: - { - const char* s; - lcdDrawText(INDENT_WIDTH, y, STR_START, menuHorizontalPosition < 0 ? attr : 0); - for (uint8_t i = 0; i < NUM_FUNCTIONS_SWITCHES; i++) { - uint8_t startPos = (g_model.functionSwitchStartConfig >> 2 * i) & 0x03; - s = _fct_sw_start[(g_model.functionSwitchStartConfig >> 2 * i) & 0x03]; - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN - (2 + FW) + i * 2 * FW, y, i + 1, 0); - lcdDrawText(lcdNextPos, y, s, attr && (menuHorizontalPosition == i) ? (s_editMode ? INVERS + BLINK : INVERS) : 0); - if (attr && menuHorizontalPosition == i) { - CHECK_INCDEC_MODELVAR(event, startPos, 0, 2); + case ITEM_MODEL_SETUP_GROUP1_LABEL: + case ITEM_MODEL_SETUP_GROUP2_LABEL: + case ITEM_MODEL_SETUP_GROUP3_LABEL: + { + int group = (k - ITEM_MODEL_SETUP_GROUP1_LABEL) / 3 + 1; + lcdDrawText(INDENT_WIDTH, y, STR_GROUP); + lcdDrawNumber(lcdNextPos, y, group, 0); + } + break; + + case ITEM_MODEL_SETUP_GROUP1_ALWAYS_ON: + case ITEM_MODEL_SETUP_GROUP2_ALWAYS_ON: + case ITEM_MODEL_SETUP_GROUP3_ALWAYS_ON: + { + uint8_t group = (k - ITEM_MODEL_SETUP_GROUP1_ALWAYS_ON) / 3 + 1; + lcdDrawText(INDENT_WIDTH * 2, y, STR_GROUP_ALWAYS_ON); + int groupAlwaysOn = IS_FSWITCH_GROUP_ON(group); + groupAlwaysOn = editCheckBox(groupAlwaysOn, MODEL_SETUP_2ND_COLUMN, y, nullptr, attr, event); + if (attr && checkIncDec_Ret) { + SET_FSWITCH_GROUP_ON(group, groupAlwaysOn); + setGroupSwitchState(group); } + } + break; + + case ITEM_MODEL_SETUP_GROUP1_START: + case ITEM_MODEL_SETUP_GROUP2_START: + case ITEM_MODEL_SETUP_GROUP3_START: + { + uint8_t group = (k - ITEM_MODEL_SETUP_GROUP1_START) / 3 + 1; + lcdDrawText(INDENT_WIDTH * 2, y, STR_START); + int sw = groupDefaultSwitch(group) + 1; + cfsGroup = group; + sw = editChoice(MODEL_SETUP_2ND_COLUMN + 1, y, nullptr, STR_FSSWITCHES, sw, 0, IS_FSWITCH_GROUP_ON(group) ? NUM_FUNCTIONS_SWITCHES : NUM_FUNCTIONS_SWITCHES + 1, attr, event, 0, checkCFSSwitchAvailable); if (attr && checkIncDec_Ret) { - swconfig_t mask = (swconfig_t)0x03 << (2*i); - g_model.functionSwitchStartConfig = (g_model.functionSwitchStartConfig & ~mask) | ((swconfig_t(startPos) & 0x03) << (2*i)); - storageDirty(EE_MODEL); + for (int i = 0; i < NUM_FUNCTIONS_SWITCHES; i += 1) { + if (FSWITCH_GROUP(i) == group) { + FSWITCH_SET_STARTUP(i, sw ? FS_START_OFF : FS_START_PREVIOUS); + } + } + if (sw > 0 && sw <= NUM_FUNCTIONS_SWITCHES) { + FSWITCH_SET_STARTUP(sw - 1, FS_START_ON); + } } } break; - } #endif + case ITEM_MODEL_SETUP_EXTENDED_LIMITS: g_model.extendedLimits = editCheckBox(g_model.extendedLimits, MODEL_SETUP_2ND_COLUMN, y, STR_ELIMITS, attr, event); break; diff --git a/radio/src/gui/128x64/radio_diagkeys.cpp b/radio/src/gui/128x64/radio_diagkeys.cpp index 91cf9a6b9d4..0364ed6010d 100644 --- a/radio/src/gui/128x64/radio_diagkeys.cpp +++ b/radio/src/gui/128x64/radio_diagkeys.cpp @@ -63,12 +63,12 @@ void menuRadioDiagFS(event_t event) lcdDrawText(FS_3RD_COLUMN, MENU_HEADER_HEIGHT + 1, "Led"); for(uint8_t i=0; i < NUM_FUNCTIONS_SWITCHES; i++) { - coord_t y = 2*FH + i*FH; + coord_t y = 2*FH + i*FH + 1; lcdDrawTextIndented(y, STR_CHAR_SWITCH); lcdDrawText(lcdNextPos, y, switchGetName(i+switchGetMaxSwitches()), 0); - lcdDrawNumber(FS_1ST_COLUMN + 2, y, getFSPhysicalState(i)); - lcdDrawNumber(FS_2ND_COLUMN + 5, y, getFSLogicalState(i)); - lcdDrawNumber(FS_3RD_COLUMN + 5, y, getFSLedState(i)); + lcdDrawText(FS_1ST_COLUMN + 7, y, getFSPhysicalState(i) ? STR_CHAR_DOWN : STR_CHAR_UP); + lcdDrawText(FS_2ND_COLUMN + 5, y, getFSLogicalState(i) ? STR_CHAR_DOWN : STR_CHAR_UP); + lcdDrawText(FS_3RD_COLUMN, y, STR_OFFON[getFSLedState(i)]); } } #endif diff --git a/radio/src/myeeprom.h b/radio/src/myeeprom.h index 5daec4f8bd9..02834701a90 100644 --- a/radio/src/myeeprom.h +++ b/radio/src/myeeprom.h @@ -95,8 +95,13 @@ #if defined(FUNCTION_SWITCHES) #define FSW_CFG_BITS 2 #define FSWITCH_CONFIG(x) (bfGet(g_model.functionSwitchConfig, FSW_CFG_BITS * (x), FSW_CFG_BITS)) - #define FSWITCH_GROUP(x) (bfGet(g_model.functionSwitchGroup, FSW_CFG_BITS * (x), FSW_CFG_BITS)) - #define IS_FSWITCH_GROUP_ON(x) (bfGet(g_model.functionSwitchGroup, FSW_CFG_BITS * NUM_FUNCTIONS_SWITCHES + x, 1)) + #define FSWITCH_SET_CONFIG(x,v) g_model.functionSwitchConfig = bfSet(g_model.functionSwitchConfig, v, FSW_CFG_BITS * (x), FSW_CFG_BITS) + #define FSWITCH_GROUP(x) (bfGet(g_model.functionSwitchGroup, FSW_CFG_BITS * (x), FSW_CFG_BITS)) + #define FSWITCH_SET_GROUP(x,v) g_model.functionSwitchGroup = bfSet(g_model.functionSwitchGroup, v, FSW_CFG_BITS * (x), FSW_CFG_BITS) + #define FSWITCH_STARTUP(x) (bfGet(g_model.functionSwitchStartConfig, FSW_CFG_BITS * (x), FSW_CFG_BITS)) + #define FSWITCH_SET_STARTUP(x,v) g_model.functionSwitchStartConfig = bfSet(g_model.functionSwitchStartConfig, v, FSW_CFG_BITS * (x), FSW_CFG_BITS) + #define IS_FSWITCH_GROUP_ON(x) (bfGet(g_model.functionSwitchGroup, FSW_CFG_BITS * NUM_FUNCTIONS_SWITCHES + x, 1)) + #define SET_FSWITCH_GROUP_ON(x,v) g_model.functionSwitchGroup = bfSet(g_model.functionSwitchGroup, v, FSW_CFG_BITS * NUM_FUNCTIONS_SWITCHES + x, 1) #define IS_SWITCH_FS(x) (x >= switchGetMaxSwitches() && x < (switchGetMaxSwitches() + switchGetMaxFctSwitches())) #define SWITCH_EXISTS(x) (IS_SWITCH_FS(x) ? true : (SWITCH_CONFIG(x) != SWITCH_NONE)) #define IS_CONFIG_3POS(x) (IS_SWITCH_FS(x) ? (FSWITCH_CONFIG(x - switchGetMaxSwitches()) == SWITCH_3POS) : (SWITCH_CONFIG(x) == SWITCH_3POS)) diff --git a/radio/src/opentx_constants.h b/radio/src/opentx_constants.h index de417886df8..2efb67d44ef 100644 --- a/radio/src/opentx_constants.h +++ b/radio/src/opentx_constants.h @@ -40,8 +40,8 @@ enum FlexAnalogConfig { }; enum fsStartPositionType { - FS_START_UP, - FS_START_DOWN, + FS_START_ON, + FS_START_OFF, FS_START_PREVIOUS }; diff --git a/radio/src/switches.cpp b/radio/src/switches.cpp index 76da6a5dded..ce6e04afba6 100644 --- a/radio/src/switches.cpp +++ b/radio/src/switches.cpp @@ -91,11 +91,11 @@ void setFSStartupPosition() for (uint8_t i = 0; i < NUM_FUNCTIONS_SWITCHES; i++) { uint8_t startPos = (g_model.functionSwitchStartConfig >> 2 * i) & 0x03; switch(startPos) { - case FS_START_DOWN: + case FS_START_OFF: g_model.functionSwitchLogicalState &= ~(1 << i); // clear state break; - case FS_START_UP: + case FS_START_ON: g_model.functionSwitchLogicalState |= 1 << i; break; @@ -117,6 +117,14 @@ uint8_t getFSLogicalState(uint8_t index) return (uint8_t )(bfSingleBitGet(getFSLogicalState(), index) >> (index)); } +void setFSLogicalState(uint8_t index, uint8_t value) +{ + if (value) + g_model.functionSwitchLogicalState |= 1 << index; // Set bit + else + g_model.functionSwitchLogicalState &= ~(1 << index); // clear state +} + uint8_t getFSPhysicalState(uint8_t index) { index += switchGetMaxSwitches(); @@ -172,6 +180,63 @@ void evalFunctionSwitches() } } } + +bool groupHasSwitchOn(uint8_t group) +{ + for (int j = 0; j < NUM_FUNCTIONS_SWITCHES; j += 1) + if (FSWITCH_GROUP(j) == group && getFSLogicalState(j)) + return true; + return false; +} + +int firstSwitchInGroup(uint8_t group) +{ + for (int j = 0; j < NUM_FUNCTIONS_SWITCHES; j += 1) + if (FSWITCH_GROUP(j) == group) + return j; + return -1; +} + +int groupDefaultSwitch(uint8_t group) +{ + bool allOff = true; + for (int j = 0; j < NUM_FUNCTIONS_SWITCHES; j += 1) { + if (FSWITCH_GROUP(j) == group) { + if (FSWITCH_STARTUP(j) == FS_START_ON) + return j; + if (FSWITCH_STARTUP(j) != FS_START_OFF) + allOff = false; + } + } + if (allOff) + return NUM_FUNCTIONS_SWITCHES; + return -1; +} + +void setGroupSwitchState(uint8_t group, int defaultSwitch) +{ + // Check rules for always on group + // - Toggle switch type not valid, change all switches to 2POS + // - One switch must be turned on, turn on first switch if needed + if (IS_FSWITCH_GROUP_ON(group)) { + for (int j = 0; j < NUM_FUNCTIONS_SWITCHES; j += 1) { + if (FSWITCH_GROUP(j) == group) { + FSWITCH_SET_CONFIG(j, SWITCH_2POS); // Toggle not valid + } + } + if (!groupHasSwitchOn(group)) { + int sw = firstSwitchInGroup(group); + if (sw >= 0) + setFSLogicalState(sw, 1); // Make sure a switch is on + } + if (groupDefaultSwitch(group) == NUM_FUNCTIONS_SWITCHES) { + // Start state for all switches is off - set all to 'last' + for (int j = 0; j < NUM_FUNCTIONS_SWITCHES; j += 1) + if (FSWITCH_GROUP(j) == group) + FSWITCH_SET_STARTUP(j, FS_START_PREVIOUS); + } + } +} #else uint8_t getFSLogicalState(uint8_t) { return false; } #endif // FUNCTION_SWITCHES diff --git a/radio/src/switches.h b/radio/src/switches.h index 029f55af56f..76ee5bb2d03 100644 --- a/radio/src/switches.h +++ b/radio/src/switches.h @@ -84,3 +84,12 @@ const char* switchGetCustomName(uint8_t idx); bool switchHasCustomName(uint8_t idx); SwitchConfig switchGetMaxType(uint8_t idx); + +#if defined(FUNCTION_SWITCHES) +uint8_t getFSLogicalState(uint8_t index); +void setFSLogicalState(uint8_t index, uint8_t value); +bool groupHasSwitchOn(uint8_t group); +int firstSwitchInGroup(uint8_t group); +int groupDefaultSwitch(uint8_t group); +void setGroupSwitchState(uint8_t group, int defaultSwitch = -1); +#endif diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index ba6ccdaa819..b2e0f80c3fc 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -95,6 +95,7 @@ ISTR(PPM_POL); ISTR(SBUS_INVERSION_VALUES); ISTR(SPORT_MODES); ISTR(FSGROUPS); +ISTR(FSSWITCHES); ISTR(ADCFILTERVALUES); ISTR(TIMER_DIR); ISTR(PREFLIGHT_POTSLIDER_CHECK); @@ -160,7 +161,11 @@ const char STR_PPMFRAME[] = TR_PPMFRAME; const char STR_REFRESHRATE[] = TR_REFRESHRATE; const char STR_MS[] = TR_MS; const char STR_SWITCH[] = TR_SWITCH; +#if defined(FUNCTION_SWITCHES) const char STR_FUNCTION_SWITCHES[] = TR_FUNCTION_SWITCHES; +const char STR_GROUP[] = TR_GROUP; +const char STR_GROUP_ALWAYS_ON[] = TR_GROUP_ALWAYS_ON; +#endif const char STR_ADJUST_GVAR[] = TR_ADJUST_GVAR; const char STR_PLAY_TRACK[] = TR_PLAY_TRACK; const char STR_PLAY_VALUE[] = TR_PLAY_VALUE; diff --git a/radio/src/translations.h b/radio/src/translations.h index 7f444d1792b..5cd1802ae96 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -212,6 +212,7 @@ extern const char* const STR_MULTI_DSM_CLONE[]; extern const char* const STR_MULTI_WBUS_MODE[]; extern const char* const STR_SPORT_MODES[]; extern const char* const STR_FSGROUPS[]; +extern const char* const STR_FSSWITCHES[]; extern const char STR_AFHDS3_ONE_TO_ONE_TELEMETRY[]; extern const char STR_AFHDS3_ONE_TO_MANY[]; @@ -293,6 +294,8 @@ extern const char STR_REFRESHRATE[]; extern const char STR_MS[]; extern const char STR_SWITCH[]; extern const char STR_FUNCTION_SWITCHES[]; +extern const char STR_GROUP[]; +extern const char STR_GROUP_ALWAYS_ON[]; extern const char STR_ADJUST_GVAR[]; extern const char STR_PLAY_TRACK[]; extern const char STR_PLAY_VALUE[]; diff --git a/radio/src/translations/cn.h b/radio/src/translations/cn.h index e24ac1a551b..28a334fe9f7 100644 --- a/radio/src/translations/cn.h +++ b/radio/src/translations/cn.h @@ -280,6 +280,8 @@ #define TR_MS "ms" #define TR_SWITCH "开关" #define TR_FUNCTION_SWITCHES "可自定义开关" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Trigger" #define TR_TRIMS "微调" #define TR_FADEIN "渐入" diff --git a/radio/src/translations/cz.h b/radio/src/translations/cz.h index 6078562dd42..592be3a16f7 100644 --- a/radio/src/translations/cz.h +++ b/radio/src/translations/cz.h @@ -295,6 +295,8 @@ #define TR_MS "ms" #define TR_SWITCH "Spínač" #define TR_FUNCTION_SWITCHES "Nastavitelné přepínače" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Trigger" #define TR_TRIMS "Trimy" #define TR_FADEIN "Přechod Zap" diff --git a/radio/src/translations/da.h b/radio/src/translations/da.h index 87a9d773b55..d1ccc41f557 100644 --- a/radio/src/translations/da.h +++ b/radio/src/translations/da.h @@ -288,6 +288,8 @@ #define TR_MS "ms" #define TR_SWITCH "Kontakt" #define TR_FUNCTION_SWITCHES "Kontakter der kan tilpasses" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Udløser" #define TR_TRIMS "Trim" #define TR_FADEIN "Tone ind" diff --git a/radio/src/translations/de.h b/radio/src/translations/de.h index 50ed5d63620..7516fd27433 100644 --- a/radio/src/translations/de.h +++ b/radio/src/translations/de.h @@ -286,6 +286,8 @@ #define TR_MS "ms" #define TR_SWITCH TR("Schalt.", "Schalter") #define TR_FUNCTION_SWITCHES "Anpassbare Schalter" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Trigger" #define TR_TRIMS "Trimmer" #define TR_FADEIN "Langs. Ein" diff --git a/radio/src/translations/en.h b/radio/src/translations/en.h index 71c1df42f81..ac271e414cf 100644 --- a/radio/src/translations/en.h +++ b/radio/src/translations/en.h @@ -285,6 +285,8 @@ #define TR_MS "ms" #define TR_SWITCH "Switch" #define TR_FUNCTION_SWITCHES "Customizable Switches" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Trigger" #define TR_TRIMS "Trims" #define TR_FADEIN "Fade in" diff --git a/radio/src/translations/es.h b/radio/src/translations/es.h index 84d11bc36df..ae539599d88 100644 --- a/radio/src/translations/es.h +++ b/radio/src/translations/es.h @@ -283,6 +283,8 @@ #define TR_MS "ms" #define TR_SWITCH TR("Interr.", "Interruptor") #define TR_FUNCTION_SWITCHES "Customizable switches" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Trigger" #define TR_TRIMS "Trims" #define TR_FADEIN "Inicio" diff --git a/radio/src/translations/fi.h b/radio/src/translations/fi.h index ca437831c40..866ec721ddb 100644 --- a/radio/src/translations/fi.h +++ b/radio/src/translations/fi.h @@ -297,6 +297,8 @@ #define TR_MS "ms" #define TR_SWITCH "Switch" #define TR_FUNCTION_SWITCHES "Customizable switches" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Trigger" #define TR_TRIMS "Trims" #define TR_FADEIN "Fade In" diff --git a/radio/src/translations/he.h b/radio/src/translations/he.h index bc9ef8cf349..bafe096ff67 100644 --- a/radio/src/translations/he.h +++ b/radio/src/translations/he.h @@ -289,6 +289,8 @@ #define TR_MS "ms" #define TR_SWITCH "מתג" #define TR_FUNCTION_SWITCHES "מפסקים בהתאמה אישית" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "הדק" #define TR_TRIMS "קיזוזים" #define TR_FADEIN "Fade in" diff --git a/radio/src/translations/it.h b/radio/src/translations/it.h index f566bffef64..9c63617e7ca 100644 --- a/radio/src/translations/it.h +++ b/radio/src/translations/it.h @@ -285,6 +285,8 @@ #define TR_MS "ms" #define TR_SWITCH "Inter." #define TR_FUNCTION_SWITCHES "Interruttori personalizzabili" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Trigger" #define TR_TRIMS "Trims" #define TR_FADEIN "Diss.In" diff --git a/radio/src/translations/jp.h b/radio/src/translations/jp.h index 7ddebe9d180..0383975b5a8 100644 --- a/radio/src/translations/jp.h +++ b/radio/src/translations/jp.h @@ -284,6 +284,8 @@ #define TR_MS "ms" #define TR_SWITCH "スイッチ" #define TR_FUNCTION_SWITCHES "カスタマイズ スイッチ" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "トリガー" #define TR_TRIMS "トリム" #define TR_FADEIN "フェードイン" diff --git a/radio/src/translations/nl.h b/radio/src/translations/nl.h index cfb0991617b..d9f6e259e69 100644 --- a/radio/src/translations/nl.h +++ b/radio/src/translations/nl.h @@ -282,6 +282,8 @@ #define TR_MS "ms" #define TR_SWITCH TR("Schak.", "Schakelaar") #define TR_FUNCTION_SWITCHES "Customizable switches" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Trigger" #define TR_TRIMS "Trims" #define TR_FADEIN "Fade in" diff --git a/radio/src/translations/pl.h b/radio/src/translations/pl.h index 3b6f9bc52f4..8ed8b1c30ee 100644 --- a/radio/src/translations/pl.h +++ b/radio/src/translations/pl.h @@ -282,6 +282,8 @@ #define TR_MS "ms" #define TR_SWITCH "Przełą" #define TR_FUNCTION_SWITCHES "Ustawiane przełączniki" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Trigger" #define TR_TRIMS "Trymy" #define TR_FADEIN "Pojawia" diff --git a/radio/src/translations/pt.h b/radio/src/translations/pt.h index 0eb4042acc5..0ac956ec3d7 100644 --- a/radio/src/translations/pt.h +++ b/radio/src/translations/pt.h @@ -288,6 +288,8 @@ #define TR_MS "ms" #define TR_SWITCH "Chave" #define TR_FUNCTION_SWITCHES "Chaves customizáveis" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Disparo" #define TR_TRIMS "Trims" #define TR_FADEIN "Aparecer" diff --git a/radio/src/translations/ru.h b/radio/src/translations/ru.h index 139ccf93a8a..aa242dd1e50 100644 --- a/radio/src/translations/ru.h +++ b/radio/src/translations/ru.h @@ -287,6 +287,8 @@ #define TR_MS "ms" #define TR_SWITCH "Тумблер" #define TR_FUNCTION_SWITCHES "Настр тумблеры" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Триггер" #define TR_TRIMS "тримы" #define TR_FADEIN "Затух входа" diff --git a/radio/src/translations/se.h b/radio/src/translations/se.h index d307bcc0267..ac8cd0fe58c 100644 --- a/radio/src/translations/se.h +++ b/radio/src/translations/se.h @@ -298,6 +298,8 @@ #define TR_MS "ms" #define TR_SWITCH "Brytare" #define TR_FUNCTION_SWITCHES "Anpassningsbara brytare" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Trigger" #define TR_TRIMS "Trimmar" #define TR_FADEIN "Tona in" diff --git a/radio/src/translations/tw.h b/radio/src/translations/tw.h index bb125e95a81..4de6ac39934 100644 --- a/radio/src/translations/tw.h +++ b/radio/src/translations/tw.h @@ -285,6 +285,8 @@ #define TR_MS "ms" #define TR_SWITCH "開關" #define TR_FUNCTION_SWITCHES "可自定義開關" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Trigger" #define TR_TRIMS "微調" #define TR_FADEIN "漸入" diff --git a/radio/src/translations/ua.h b/radio/src/translations/ua.h index eb31dcfc78e..a0dc13a0d04 100644 --- a/radio/src/translations/ua.h +++ b/radio/src/translations/ua.h @@ -287,6 +287,8 @@ #define TR_MS "ms" #define TR_SWITCH "Перемикач" #define TR_FUNCTION_SWITCHES "Користувацькі перемикачі" +#define TR_GROUP "Group" +#define TR_GROUP_ALWAYS_ON "Always on" #define TR_SF_SWITCH "Тригери" #define TR_TRIMS "Трим." #define TR_FADEIN "Вх.Згасання" diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index 2c4cf218bbf..aeada9515e4 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -314,6 +314,7 @@ #define STR_CHAR_BW_DEGREE '@' #define TR_FSGROUPS "-","1","2","3" +#define TR_FSSWITCHES "=", "SW1", "SW2", "SW3", "SW4", "SW5", "SW6", TR_OFF // // HoTT Telemetry sensor names by Hott device