From 6c2f97ca68b9dddbe079d57ea989092e9b4c6fbb Mon Sep 17 00:00:00 2001 From: Neil Horne <15316949+elecpower@users.noreply.github.com> Date: Sun, 24 Nov 2024 09:20:06 +1100 Subject: [PATCH] chore(cpn): move model edit setup classes to own code files (#5678) --- companion/src/modeledit/CMakeLists.txt | 3 + companion/src/modeledit/setup.cpp | 1553 +---------------- companion/src/modeledit/setup.h | 251 +-- .../src/modeledit/setup_function_switches.cpp | 323 ++++ .../src/modeledit/setup_function_switches.h | 166 ++ companion/src/modeledit/setup_module.cpp | 1099 ++++++++++++ companion/src/modeledit/setup_module.h | 109 ++ companion/src/modeledit/setup_timer.cpp | 186 ++ companion/src/modeledit/setup_timer.h | 65 + 9 files changed, 1958 insertions(+), 1797 deletions(-) create mode 100644 companion/src/modeledit/setup_function_switches.cpp create mode 100644 companion/src/modeledit/setup_function_switches.h create mode 100644 companion/src/modeledit/setup_module.cpp create mode 100644 companion/src/modeledit/setup_module.h create mode 100644 companion/src/modeledit/setup_timer.cpp create mode 100644 companion/src/modeledit/setup_timer.h diff --git a/companion/src/modeledit/CMakeLists.txt b/companion/src/modeledit/CMakeLists.txt index 4ec7b64be..ab5ce980e 100644 --- a/companion/src/modeledit/CMakeLists.txt +++ b/companion/src/modeledit/CMakeLists.txt @@ -17,6 +17,9 @@ set(${PROJECT_NAME}_NAMES mixerslistwidget modeledit modeloptions + setup_timer + setup_module + setup_function_switches setup telemetry telemetry_customscreens diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 723226891..6c3417358 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -21,1562 +21,17 @@ #include "setup.h" #include "ui_setup.h" -#include "ui_setup_timer.h" -#include "ui_setup_module.h" -#include "ui_setup_function_switches.h" #include "appdata.h" -#include "modelprinter.h" -#include "multiprotocols.h" #include "checklistdialog.h" #include "helpers.h" -#include "moduledata.h" #include "namevalidator.h" +#include "autolineedit.h" #include -constexpr char FIM_TIMERSWITCH[] {"Timer Switch"}; -constexpr char FIM_THRSOURCE[] {"Throttle Source"}; -// constexpr char FIM_TRAINERMODE[] {"Trainer Mode"}; -constexpr char FIM_ANTENNAMODE[] {"Antenna Mode"}; -constexpr char FIM_HATSMODE[] {"Hats Mode"}; -constexpr char FIM_CRSFARMSWITCH[] {"CRSF Arming Switch"}; - - -TimerPanel::TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, - QWidget * prevFocus, FilteredItemModelFactory * panelFilteredModels, CompoundItemModelFactory * panelItemModels): - ModelPanel(parent, model, generalSettings, firmware), - timer(timer), - ui(new Ui::Timer), - modelsUpdateCnt(0) -{ - ui->setupUi(this); - connectItemModelEvents(panelFilteredModels->getItemModel(FIM_TIMERSWITCH)); - - lock = true; - - // Name - int length = firmware->getCapability(TimersName); - if (length == 0) - ui->name->hide(); - else { - ui->name->setValidator(new NameValidator(firmware->getBoard(), this)); - ui->name->setField(timer.name, length, this); - connect(ui->name, SIGNAL(currentDataChanged()), this, SLOT(onNameChanged())); - } - - ui->value->setField(timer.val, this); - ui->value->setMaximumTime(firmware->getMaxTimerStart()); - connect(ui->value, &AutoTimeEdit::currentDataChanged, [=](const int val) { - update(); - }); - - ui->mode->setModel(panelItemModels->getItemModel(AIM_TIMER_MODE)); - ui->mode->setField(timer.mode, this); - connect(ui->mode, SIGNAL(currentDataChanged(int)), this, SLOT(onModeChanged(int))); - - ui->swtch->setModel(panelFilteredModels->getItemModel(FIM_TIMERSWITCH)); - ui->swtch->setField(timer.swtch, this); - - ui->countdownBeep->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNBEEP)); - ui->countdownBeep->setField(timer.countdownBeep, this); - connect(ui->countdownBeep, SIGNAL(currentDataChanged(int)), this, SLOT(onCountdownBeepChanged(int))); - - ui->minuteBeep->setField(timer.minuteBeep, this); - - if (firmware->getCapability(PermTimers)) { - ui->persistent->setModel(panelItemModels->getItemModel(AIM_TIMER_PERSISTENT)); - ui->persistent->setField(timer.persistent, this); - } - else { - ui->persistent->hide(); - ui->persistentValue->hide(); - } - - ui->countdownStart->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNSTART)); - ui->countdownStart->setField(timer.countdownStart, this); - - - ui->showElapsed->setModel(panelItemModels->getItemModel(AIM_TIMER_SHOWELAPSED)); - ui->showElapsed->setField(timer.showElapsed, this); - - disableMouseScrolling(); - QWidget::setTabOrder(prevFocus, ui->name); - QWidget::setTabOrder(ui->name, ui->value); - QWidget::setTabOrder(ui->value, ui->mode); - QWidget::setTabOrder(ui->mode, ui->countdownBeep); - QWidget::setTabOrder(ui->countdownBeep, ui->countdownStart); - QWidget::setTabOrder(ui->countdownStart, ui->minuteBeep); - QWidget::setTabOrder(ui->minuteBeep, ui->persistent); - QWidget::setTabOrder(ui->persistent, ui->showElapsed); - - update(); - lock = false; -} - -TimerPanel::~TimerPanel() -{ - delete ui; -} - -void TimerPanel::update() -{ - lock = true; - - ui->name->updateValue(); - ui->mode->updateValue(); - ui->swtch->updateValue(); - ui->value->updateValue(); - ui->countdownBeep->updateValue(); - ui->minuteBeep->updateValue(); - ui->countdownStart->updateValue(); - ui->showElapsed->updateValue(); - - if (timer.mode != TimerData::TIMERMODE_OFF) - ui->swtch->setEnabled(true); - else - ui->swtch->setEnabled(false); - - if (timer.countdownBeep == TimerData::COUNTDOWNBEEP_SILENT) { - ui->countdownStartLabel->setEnabled(false); - ui->countdownStart->setEnabled(false); - } - else { - ui->countdownStartLabel->setEnabled(true); - ui->countdownStart->setEnabled(true); - } - - if (firmware->getCapability(PermTimers)) { - ui->persistent->updateValue(); - ui->persistentValue->setText(timer.pvalueToString()); - } - - if (timer.val) { - ui->showElapsed->setEnabled(true); - } - else { - ui->showElapsed->setEnabled(false); - } - - lock = false; -} - -QWidget * TimerPanel::getLastFocus() -{ - return ui->persistent; -} - -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; - modelsUpdateCnt++; -} - -void TimerPanel::onItemModelUpdateComplete() -{ - modelsUpdateCnt--; - if (modelsUpdateCnt < 1) { - update(); - lock = false; - } -} - -void TimerPanel::onNameChanged() -{ - emit nameChanged(); -} - -void TimerPanel::onCountdownBeepChanged(int index) -{ - timer.countdownBeepChanged(); - update(); -} - -void TimerPanel::onModeChanged(int index) -{ - timer.modeChanged(); - update(); - emit modeChanged(); -} - -/******************************************************************************/ - -#define FAILSAFE_CHANNEL_HOLD 2000 -#define FAILSAFE_CHANNEL_NOPULSE 2001 - -#define MASK_PROTOCOL (1<<0) -#define MASK_CHANNELS_COUNT (1<<1) -#define MASK_RX_NUMBER (1<<2) -#define MASK_CHANNELS_RANGE (1<<3) -#define MASK_PPM_FIELDS (1<<4) -#define MASK_FAILSAFES (1<<5) -#define MASK_OPEN_DRAIN (1<<6) -#define MASK_MULTIMODULE (1<<7) -#define MASK_ANTENNA (1<<8) -#define MASK_MULTIOPTION (1<<9) -#define MASK_R9M (1<<10) -#define MASK_SBUSPPM_FIELDS (1<<11) -#define MASK_SUBTYPES (1<<12) -#define MASK_ACCESS (1<<13) -#define MASK_RX_FREQ (1<<14) -#define MASK_RF_POWER (1<<15) -#define MASK_RF_RACING_MODE (1<<16) -#define MASK_GHOST (1<<17) -#define MASK_BAUDRATE (1<<18) -#define MASK_MULTI_DSM_OPT (1<<19) -#define MASK_CHANNELMAP (1<<20) -#define MASK_MULTI_BAYANG_OPT (1<<21) -#define MASK_AFHDS (1<<22) -#define MASK_CSRF_ARMING_MODE (1<<23) -#define MASK_CSRF_ARMING_TRIGGER (1<<24) - -quint8 ModulePanel::failsafesValueDisplayType = ModulePanel::FAILSAFE_DISPLAY_PERCENT; - -ModulePanel::ModulePanel(QWidget * parent, ModelData & model, ModuleData & module, GeneralSettings & generalSettings, Firmware * firmware, int moduleIdx, - FilteredItemModelFactory * panelFilteredItemModels, CompoundItemModelFactory * panelItemModels): - ModelPanel(parent, model, generalSettings, firmware), - module(module), - moduleIdx(moduleIdx), - ui(new Ui::Module), - trainerModeItemModel(nullptr) -{ - lock = true; - - ui->setupUi(this); - - ui->label_module->setText(ModuleData::indexToString(moduleIdx, firmware)); - if (isTrainerModule(moduleIdx)) { - ui->formLayout_col1->setSpacing(0); - if (!IS_HORUS_OR_TARANIS(firmware->getBoard())) { - ui->label_trainerMode->hide(); - ui->trainerMode->hide(); - } - else { - updateTrainerModeItemModel(); - ui->trainerMode->setField(model.trainerMode); - connect(ui->trainerMode, &AutoComboBox::currentDataChanged, this, [=] () { - update(); - emit updateItemModels(); - emit modified(); - }); - } - } - else { - ui->label_trainerMode->hide(); - ui->trainerMode->hide(); - } - - if (panelFilteredItemModels) { - if (!isTrainerModule(moduleIdx)) { - int id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(ModuleData::protocolItemModel(generalSettings), moduleIdx + 1/*flag cannot be 0*/), QString("Module Protocol %1").arg(moduleIdx)); - panelFilteredItemModels->getItemModel(id)->setSortCaseSensitivity(Qt::CaseInsensitive); - panelFilteredItemModels->getItemModel(id)->sort(0); - ui->protocol->setModel(panelFilteredItemModels->getItemModel(id)); - - if (ui->protocol->findData(module.protocol) < 0) { - const QString moduleIdxDesc = isInternalModule(moduleIdx) ? tr("internal") : tr("external"); - const QString compareDesc = isInternalModule(moduleIdx) ? tr("hardware") : tr("profile"); - const QString intModuleDesc = isInternalModule(moduleIdx) ? ModuleData::typeToString(generalSettings.internalModule) : ""; - QString msg = tr("Warning: The %1 module protocol %2 is incompatible with the %3 %1 module %4 and has been set to OFF!"); - msg = msg.arg(moduleIdxDesc).arg(module.protocolToString(module.protocol)).arg(compareDesc).arg(intModuleDesc); - - QMessageBox *msgBox = new QMessageBox(this); - msgBox->setIcon( QMessageBox::Warning ); - msgBox->setText(msg); - msgBox->addButton( "Ok", QMessageBox::AcceptRole ); - msgBox->setWindowFlag(Qt::WindowStaysOnTopHint); - msgBox->setAttribute(Qt::WA_DeleteOnClose); // delete pointer after close - msgBox->setModal(false); - msgBox->show(); - - module.clear(); - } - - ui->protocol->setField(module.protocol, this); - } - - if (isInternalModule(moduleIdx)) { - int id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(GeneralSettings::antennaModeItemModel(true)), FIM_ANTENNAMODE); - ui->antennaMode->setModel(panelFilteredItemModels->getItemModel(id)); - } - } - - ui->multiProtocol->setModel(Multiprotocols::protocolItemModel()); - - ui->btnGrpValueType->setId(ui->optPercent, FAILSAFE_DISPLAY_PERCENT); - ui->btnGrpValueType->setId(ui->optUs, FAILSAFE_DISPLAY_USEC); - ui->btnGrpValueType->button(failsafesValueDisplayType)->setChecked(true); - - ui->registrationId->setText(model.registrationId); - - if (panelItemModels) { - ui->crsfArmingMode->setModel(panelItemModels->getItemModel(AIM_MODULE_CRSFARMINGMODE)); - ui->crsfArmingTrigger->setModel(panelFilteredItemModels->getItemModel(FIM_CRSFARMSWITCH)); - } - - setupFailsafes(); - - disableMouseScrolling(); - - update(); - - connect(ui->protocol, &AutoComboBox::currentDataChanged, this, &ModulePanel::onProtocolChanged); - connect(ui->multiSubType, static_cast(&QComboBox::currentIndexChanged), this, &ModulePanel::onSubTypeChanged); - connect(ui->multiProtocol, static_cast(&QComboBox::currentIndexChanged), this, &ModulePanel::onMultiProtocolChanged); - connect(this, &ModulePanel::channelsRangeChanged, this, &ModulePanel::setupFailsafes); - connect(ui->btnGrpValueType, static_cast(&QButtonGroup::idClicked), this, &ModulePanel::onFailsafesDisplayValueTypeChanged); - connect(ui->rxFreq, static_cast(&QSpinBox::valueChanged), this, &ModulePanel::onRfFreqChanged); - connect(ui->clearRx1, SIGNAL(clicked()), this, SLOT(onClearAccessRxClicked())); - connect(ui->clearRx2, SIGNAL(clicked()), this, SLOT(onClearAccessRxClicked())); - connect(ui->clearRx3, SIGNAL(clicked()), this, SLOT(onClearAccessRxClicked())); - connect(ui->cboAfhdsOpt1, static_cast(&QComboBox::currentIndexChanged), [=] (int index) - { - if (lock) - return; - - if (this->module.protocol == PULSES_FLYSKY_AFHDS2A) - Helpers::setBitmappedValue(this->module.flysky.mode, ui->cboAfhdsOpt1->currentData().toInt(), 1); - else - this->module.afhds3.phyMode = ui->cboAfhdsOpt1->currentData().toInt(); - - emit modified(); - }); - connect(ui->cboAfhdsOpt2, static_cast(&QComboBox::currentIndexChanged), [=] (int index) - { - if (lock) - return; - - if (this->module.protocol == PULSES_FLYSKY_AFHDS2A) - Helpers::setBitmappedValue(this->module.flysky.mode, ui->cboAfhdsOpt2->currentData().toInt(), 0); - else - this->module.afhds3.emi = ui->cboAfhdsOpt2->currentData().toInt(); - - emit modified(); - }); - - connect(ui->crsfArmingMode, static_cast(&QComboBox::currentIndexChanged), [=] (int index) - { - this->module.crsf.crsfArmingMode = ui->crsfArmingMode->currentData().toInt(); - if (this->module.crsf.crsfArmingMode != ModuleData::CRSF_ARMING_MODE_SWITCH) - this->module.crsf.crsfArmingTrigger = RawSwitch(SWITCH_TYPE_NONE); - update(); - emit modified(); - }); - - connect(ui->crsfArmingTrigger, static_cast(&QComboBox::currentIndexChanged), [=] (int index) - { - this->module.crsf.crsfArmingTrigger = RawSwitch(ui->crsfArmingTrigger->currentData().toInt()); - emit modified(); - }); - - lock = false; -} - -ModulePanel::~ModulePanel() -{ - delete ui; -} - -void ModulePanel::setupFailsafes() -{ - ChannelFailsafeWidgetsGroup grp; - const int start = module.channelsStart; - const int end = start + module.channelsCount; - const bool hasFailsafe = module.hasFailsafes(firmware); - - lock = true; - - QMutableMapIterator i(failsafeGroupsMap); - while (i.hasNext()) { - i.next(); - grp = i.value(); - ui->failsafesLayout->removeWidget(grp.label); - ui->failsafesLayout->removeWidget(grp.combo); - ui->failsafesLayout->removeWidget(grp.sbPercent); - ui->failsafesLayout->removeWidget(grp.sbUsec); - if (i.key() < start || i.key() >= end || !hasFailsafe) { - grp.label->deleteLater(); - grp.combo->deleteLater(); - grp.sbPercent->deleteLater(); - grp.sbUsec->deleteLater(); - i.remove(); - } - } - - if (!hasFailsafe) { - lock = false; - return; - } - - int row = 0; - int col = 0; - int channelMax = model->getChannelsMax(); - int channelMaxUs = 512 * channelMax / 100 * 2; - - for (int i = start; i < end; ++i) { - if (failsafeGroupsMap.contains(i)) { - grp = failsafeGroupsMap.value(i); - } - else { - QLabel * label = new QLabel(this); - label->setProperty("index", i); - label->setText(QString::number(i + 1)); - - QComboBox * combo = new QComboBox(this); - combo->setProperty("index", i); - combo->addItem(tr("Value"), 0); - combo->addItem(tr("Hold"), FAILSAFE_CHANNEL_HOLD); - combo->addItem(tr("No Pulse"), FAILSAFE_CHANNEL_NOPULSE); - - QDoubleSpinBox * sbDbl = new QDoubleSpinBox(this); - sbDbl->setProperty("index", i); - sbDbl->setMinimumSize(QSize(20, 0)); - sbDbl->setRange(-channelMax, channelMax); - sbDbl->setSingleStep(0.1); - sbDbl->setDecimals(1); - - QSpinBox * sbInt = new QSpinBox(this); - sbInt->setProperty("index", i); - sbInt->setMinimumSize(QSize(20, 0)); - sbInt->setRange(-channelMaxUs, channelMaxUs); - sbInt->setSingleStep(1); - - grp = ChannelFailsafeWidgetsGroup(); - grp.combo = combo; - grp.sbPercent = sbDbl; - grp.sbUsec = sbInt; - grp.label = label; - failsafeGroupsMap.insert(i, grp); - - connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(onFailsafeComboIndexChanged(int))); - connect(sbInt, SIGNAL(valueChanged(int)), this, SLOT(onFailsafeUsecChanged(int))); - connect(sbDbl, SIGNAL(valueChanged(double)), this, SLOT(onFailsafePercentChanged(double))); - } - - ui->failsafesLayout->addWidget(grp.label, row, col, Qt::AlignHCenter); - ui->failsafesLayout->addWidget(grp.combo, row + 1, col, Qt::AlignHCenter); - ui->failsafesLayout->addWidget(grp.sbPercent, row + 2, col, Qt::AlignHCenter); - ui->failsafesLayout->addWidget(grp.sbUsec, row + 3, col, Qt::AlignHCenter); - grp.sbPercent->setVisible(failsafesValueDisplayType == FAILSAFE_DISPLAY_PERCENT); - grp.sbUsec->setVisible(failsafesValueDisplayType == FAILSAFE_DISPLAY_USEC); - - updateFailsafe(i); - - if (++col > 7) { - row += 4; - col = 0; - } - } - - lock = false; -} - -void ModulePanel::update() -{ - lock = true; - - const auto protocol = (PulsesProtocol)module.protocol; - const auto board = firmware->getBoard(); - const auto & pdef = multiProtocols.getProtocol(module.multi.rfProtocol); - unsigned int mask = 0; - unsigned int max_rx_num = 63; - - if (!isTrainerModule(moduleIdx)) { - mask |= MASK_PROTOCOL; - switch (protocol) { - case PULSES_PXX_R9M: - mask |= MASK_R9M | MASK_RF_POWER | MASK_SUBTYPES; - case PULSES_ACCESS_R9M: - case PULSES_ACCESS_R9M_LITE: - case PULSES_ACCESS_R9M_LITE_PRO: - case PULSES_ACCESS_ISRM: - case PULSES_ACCST_ISRM_D16: - case PULSES_XJT_LITE_X16: - case PULSES_XJT_LITE_D8: - case PULSES_XJT_LITE_LR12: - case PULSES_PXX_XJT_X16: - case PULSES_PXX_XJT_D8: - case PULSES_PXX_XJT_LR12: - case PULSES_PXX_DJT: - mask |= MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT; - // ACCST Rx ID - if (protocol==PULSES_PXX_XJT_X16 || protocol==PULSES_PXX_XJT_LR12 || - protocol==PULSES_PXX_R9M || protocol==PULSES_ACCST_ISRM_D16 || - protocol==PULSES_XJT_LITE_X16 || protocol==PULSES_XJT_LITE_LR12) - mask |= MASK_RX_NUMBER; - // ACCESS - else if (protocol==PULSES_ACCESS_ISRM || protocol==PULSES_ACCESS_R9M || - protocol==PULSES_ACCESS_R9M_LITE || protocol==PULSES_ACCESS_R9M_LITE_PRO) - mask |= MASK_RX_NUMBER | MASK_ACCESS; - if (isInternalModule(moduleIdx) && - (protocol==PULSES_PXX_XJT_X16 || - protocol==PULSES_PXX_XJT_D8 || protocol==PULSES_PXX_XJT_LR12) && - HAS_EXTERNAL_ANTENNA(board) && generalSettings.antennaMode == GeneralSettings::ANTENNA_MODE_PER_MODEL) - mask |= MASK_ANTENNA; - if (protocol == PULSES_ACCESS_ISRM && module.channelsCount == 8) - mask |= MASK_RF_RACING_MODE; - break; - case PULSES_LP45: - case PULSES_DSM2: - case PULSES_DSMX: - mask |= MASK_CHANNELS_RANGE | MASK_RX_NUMBER; - module.channelsCount = 6; - max_rx_num = 20; - break; - case PULSES_CROSSFIRE: - mask |= MASK_CHANNELS_RANGE | MASK_RX_NUMBER | MASK_BAUDRATE | MASK_CSRF_ARMING_MODE; - module.channelsCount = 16; - ui->telemetryBaudrate->setModel(ModuleData::telemetryBaudrateItemModel(protocol)); - ui->telemetryBaudrate->setField(module.crsf.telemetryBaudrate); - ui->crsfArmingMode->setCurrentIndex(module.crsf.crsfArmingMode); - if (module.crsf.crsfArmingMode == ModuleData::CRSF_ARMING_MODE_SWITCH) { - mask |= MASK_CSRF_ARMING_TRIGGER; - ui->crsfArmingTrigger->setCurrentIndex(ui->crsfArmingTrigger->findData(RawSwitch(module.crsf.crsfArmingTrigger).toValue())); - } - break; - case PULSES_GHOST: - mask |= MASK_CHANNELS_RANGE | MASK_GHOST | MASK_BAUDRATE; - module.channelsCount = 16; - ui->telemetryBaudrate->setModel(ModuleData::telemetryBaudrateItemModel(protocol)); - ui->telemetryBaudrate->setField(module.ghost.telemetryBaudrate); - break; - case PULSES_PPM: - mask |= MASK_SUBTYPES | MASK_PPM_FIELDS | MASK_SBUSPPM_FIELDS| MASK_CHANNELS_RANGE| MASK_CHANNELS_COUNT; - if (IS_9XRPRO(board)) { - mask |= MASK_OPEN_DRAIN; - } - break; - case PULSES_SBUS: - module.channelsCount = 16; - mask |= MASK_SBUSPPM_FIELDS| MASK_CHANNELS_RANGE; - break; - case PULSES_MULTIMODULE: - mask |= MASK_CHANNELS_RANGE | MASK_RX_NUMBER | MASK_MULTIMODULE | MASK_SUBTYPES; - - switch (module.multi.rfProtocol) { - case MODULE_SUBTYPE_MULTI_OLRS: - max_rx_num = MODULE_SUBTYPE_MULTI_OLRS_RXNUM; - case MODULE_SUBTYPE_MULTI_BUGS: - max_rx_num = MODULE_SUBTYPE_MULTI_BUGS_RXNUM; - case MODULE_SUBTYPE_MULTI_BUGS_MINI: - max_rx_num = MODULE_SUBTYPE_MULTI_BUGS_MINI_RXNUM; - default: - max_rx_num = 63; - } - - if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) - mask |= MASK_CHANNELS_COUNT; - else - module.channelsCount = 16; - if (pdef.optionsstr != nullptr) { - if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) - mask |= MASK_MULTI_DSM_OPT; - else if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_BAYANG) - mask |= MASK_MULTI_BAYANG_OPT; - else - mask |= MASK_MULTIOPTION; - } - if (pdef.hasFailsafe || (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_FRSKY && (module.subType == 0 || module.subType == 2 || module.subType > 3 ))) - mask |= MASK_FAILSAFES; - if (pdef.disableChannelMap) - mask |= MASK_CHANNELMAP; - break; - case PULSES_FLYSKY_AFHDS3: - mask |= MASK_RX_NUMBER; - case PULSES_FLYSKY_AFHDS2A: - mask |= MASK_CHANNELS_RANGE| MASK_CHANNELS_COUNT | MASK_FAILSAFES | MASK_AFHDS; - break; - case PULSES_LEMON_DSMP: - mask |= MASK_CHANNELS_RANGE; - break; - default: - break; - } - - if (protocol != PULSES_MULTIMODULE && module.hasFailsafes(firmware)) - mask |= MASK_FAILSAFES; - } - else if (IS_HORUS_OR_TARANIS(board)) { - if (model->trainerMode == TRAINER_MODE_SLAVE_JACK) { - mask |= MASK_PPM_FIELDS | MASK_SBUSPPM_FIELDS | MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT; - } - } - else if (model->trainerMode != TRAINER_MODE_MASTER_JACK) { - mask |= MASK_PPM_FIELDS | MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT; - } - - if (isExternalModule(moduleIdx)) - ui->telemetryBaudrate->setVisible(mask & MASK_BAUDRATE); - else - ui->telemetryBaudrate->setVisible(false); - - ui->label_protocol->setVisible(mask & MASK_PROTOCOL); - ui->protocol->setVisible(mask & MASK_PROTOCOL); - ui->label_rxNumber->setVisible(mask & MASK_RX_NUMBER); - ui->rxNumber->setVisible(mask & MASK_RX_NUMBER); - ui->rxNumber->setMaximum(max_rx_num); - ui->rxNumber->setValue(module.modelId); - ui->label_channelsStart->setVisible(mask & MASK_CHANNELS_RANGE); - ui->channelsStart->setVisible(mask & MASK_CHANNELS_RANGE); - ui->channelsStart->setMaximum(33 - module.channelsCount); - ui->channelsStart->setValue(module.channelsStart+1); - ui->label_channelsCount->setVisible(mask & MASK_CHANNELS_RANGE); - ui->channelsCount->setVisible(mask & MASK_CHANNELS_RANGE); - ui->channelsCount->setEnabled(mask & MASK_CHANNELS_COUNT); - ui->channelsCount->setMaximum(module.getMaxChannelCount()); - ui->channelsCount->setValue(module.channelsCount); - ui->channelsCount->setSingleStep(firmware->getCapability(HasPPMStart) ? 1 : 2); - ui->label_crsfArmingMode->setVisible(mask & MASK_CSRF_ARMING_MODE); - ui->crsfArmingMode->setVisible(mask & MASK_CSRF_ARMING_MODE); - ui->crsfArmingTrigger->setVisible(mask & MASK_CSRF_ARMING_TRIGGER); - - // PPM settings fields - ui->label_ppmPolarity->setVisible(mask & MASK_SBUSPPM_FIELDS); - ui->ppmPolarity->setVisible(mask & MASK_SBUSPPM_FIELDS); - ui->ppmPolarity->setCurrentIndex(module.ppm.pulsePol); - ui->label_ppmOutputType->setVisible(mask & MASK_OPEN_DRAIN); - ui->ppmOutputType->setVisible(mask & MASK_OPEN_DRAIN); - ui->ppmOutputType->setCurrentIndex(module.ppm.outputType); - ui->label_ppmDelay->setVisible(mask & MASK_PPM_FIELDS); - ui->ppmDelay->setVisible(mask & MASK_PPM_FIELDS); - ui->ppmDelay->setValue(module.ppm.delay); - ui->label_ppmFrameLength->setVisible(mask & MASK_SBUSPPM_FIELDS); - ui->ppmFrameLength->setVisible(mask & MASK_SBUSPPM_FIELDS); - ui->ppmFrameLength->setMinimum(module.channelsCount * (model->extendedLimits ? 2.250 : 2)+3.5); - ui->ppmFrameLength->setMaximum(firmware->getCapability(PPMFrameLength)); - ui->ppmFrameLength->setValue(22.5 + ((double)module.ppm.frameLength) * 0.5); - - if (mask & MASK_ANTENNA) { - if (module.pxx.antennaMode == GeneralSettings::ANTENNA_MODE_PER_MODEL) - module.pxx.antennaMode = GeneralSettings::ANTENNA_MODE_INTERNAL; - ui->antennaMode->setField(module.pxx.antennaMode, this); - ui->antennaLabel->show(); - ui->antennaMode->show(); - } - else { - ui->antennaLabel->hide(); - ui->antennaMode->hide(); - } - - if (mask & MASK_RF_RACING_MODE) { - ui->racingMode->show(); - ui->racingMode->setChecked(module.access.racingMode); - } - else { - ui->racingMode->hide(); - } - - // R9M options - ui->r9mPower->setVisible(mask & MASK_RF_POWER); - ui->label_r9mPower->setVisible(mask & MASK_RF_POWER); - ui->warning_r9mPower->setVisible((mask & MASK_R9M) && module.subType == MODULE_SUBTYPE_R9M_EU); - ui->warning_r9mFlex->setVisible((mask & MASK_R9M) && module.subType > MODULE_SUBTYPE_R9M_EU); - - if (mask & MASK_RF_POWER) { - const QSignalBlocker blocker(ui->r9mPower); - ui->r9mPower->clear(); - ui->r9mPower->addItems(ModuleData::powerValueStrings(protocol, module.subType, firmware)); - ui->r9mPower->setCurrentIndex(mask & MASK_R9M ? module.pxx.power : module.afhds3.rfPower); - } - - // module subtype - ui->label_multiSubType->setVisible(mask & MASK_SUBTYPES); - ui->multiSubType->setVisible(mask & MASK_SUBTYPES); - if (mask & MASK_SUBTYPES) { - unsigned numEntries = 2; // R9M FCC/EU - unsigned i = 0; - switch(protocol){ - case PULSES_MULTIMODULE: - numEntries = (module.multi.rfProtocol > MODULE_SUBTYPE_MULTI_LAST ? 8 : pdef.numSubTypes()); - break; - case PULSES_PXX_R9M: - if (firmware->getCapability(HasModuleR9MFlex)) - i = 2; - break; - case PULSES_PPM: - numEntries = PPM_NUM_SUBTYPES; - break; - default: - break; - } - numEntries += i; - const QSignalBlocker blocker(ui->multiSubType); - ui->multiSubType->clear(); - for ( ; i < numEntries; i++) - ui->multiSubType->addItem(module.subTypeToString(i), i); - ui->multiSubType->setCurrentIndex(ui->multiSubType->findData(module.subType)); - } - - // Multi settings fields - ui->label_multiProtocol->setVisible(mask & MASK_MULTIMODULE); - ui->multiProtocol->setVisible(mask & MASK_MULTIMODULE); - ui->label_option->setVisible(mask & MASK_MULTIOPTION); - ui->optionValue->setVisible(mask & MASK_MULTIOPTION); - ui->lblChkOption->setVisible(mask & MASK_MULTI_DSM_OPT); - ui->chkOption->setVisible(mask & MASK_MULTI_DSM_OPT); - ui->lblCboOption->setVisible(mask & MASK_MULTI_DSM_OPT || mask & MASK_MULTI_BAYANG_OPT); - ui->cboOption->setVisible(mask & MASK_MULTI_DSM_OPT || mask & MASK_MULTI_BAYANG_OPT); - ui->disableTelem->setVisible(mask & MASK_MULTIMODULE); - ui->disableChMap->setVisible(mask & MASK_CHANNELMAP); - ui->lowPower->setVisible(mask & MASK_MULTIMODULE); - ui->autoBind->setVisible(mask & MASK_MULTIMODULE); - if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) - ui->autoBind->setVisible(false); - else - ui->autoBind->setText(tr("Bind on channel")); - - if (mask & MASK_MULTIMODULE) { - ui->multiProtocol->setCurrentIndex(ui->multiProtocol->findData(module.multi.rfProtocol)); - ui->disableTelem->setChecked(module.multi.disableTelemetry); - ui->disableChMap->setChecked(module.multi.disableMapping); - ui->autoBind->setChecked(module.multi.autoBindMode); - ui->lowPower->setChecked(module.multi.lowPowerMode); - } - - if (mask & MASK_MULTIOPTION) { - ui->label_option->setText(qApp->translate("Multiprotocols", qPrintable(pdef.optionsstr))); - int8_t min, max; - getMultiOptionValues(module.multi.rfProtocol, min, max); - - auto lineEdit = ui->optionValue->findChild(); - - if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { - ui->optionValue->setMinimum(50 + 5 * min); - ui->optionValue->setMaximum(50 + 5 * max); - ui->optionValue->setSingleStep(5); - ui->optionValue->setValue(50 + 5 * module.multi.optionValue); - if (lineEdit) { - lineEdit->setReadOnly(true); - lineEdit->setFocusPolicy(Qt::NoFocus); - } - } - else { - ui->optionValue->setMinimum(min); - ui->optionValue->setMaximum(max); - ui->optionValue->setSingleStep(1); - ui->optionValue->setValue(module.multi.optionValue); - if (lineEdit) { - lineEdit->setReadOnly(false); - lineEdit->setFocusPolicy(Qt::WheelFocus); - } - } - } - - if (mask & MASK_MULTI_DSM_OPT) { - ui->lblChkOption->setText(qApp->translate("Multiprotocols", qPrintable(pdef.optionsstr))); - ui->chkOption->setChecked(Helpers::getBitmappedValue(module.multi.optionValue, 0)); - ui->lblCboOption->setText(qApp->translate("Multiprotocols", "Servo update rate")); - ui->cboOption->clear(); - ui->cboOption->addItems({ DSM2_OPTION_SERVOFREQ_NAMES }); - ui->cboOption->setSizeAdjustPolicy(QComboBox::AdjustToContents); - ui->cboOption->setCurrentIndex(Helpers::getBitmappedValue(module.multi.optionValue, 1)); - } - - if (mask & MASK_MULTI_BAYANG_OPT) { - ui->lblCboOption->setText(qApp->translate("Multiprotocols", qPrintable(pdef.optionsstr))); - ui->cboOption->clear(); - ui->cboOption->addItems({ BAYANG_OPTION_TELEMETRY_NAMES }); - ui->cboOption->setSizeAdjustPolicy(QComboBox::AdjustToContents); - ui->cboOption->setCurrentIndex(Helpers::getBitmappedValue(module.multi.optionValue, 1)); - } - - // Ghost settings fields - ui->raw12bits->setVisible(mask & MASK_GHOST); - if (mask & MASK_GHOST) { - ui->raw12bits->setChecked(module.ghost.raw12bits); - } - - if (mask & MASK_ACCESS) { - ui->rx1->setText(module.access.receiverName[0]); - ui->rx2->setText(module.access.receiverName[1]); - ui->rx3->setText(module.access.receiverName[2]); - } - - ui->registrationIdLabel->setVisible(mask & MASK_ACCESS); - ui->registrationId->setVisible(mask & MASK_ACCESS); - - ui->rx1Label->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 0))); - ui->clearRx1->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 0))); - ui->rx1->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 0))); - - ui->rx2Label->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 1))); - ui->clearRx2->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 1))); - ui->rx2->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 1))); - - ui->rx3Label->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 2))); - ui->clearRx3->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 2))); - ui->rx3->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 2))); - - // AFHFS - if (mask & MASK_AFHDS) { - if (protocol == PULSES_FLYSKY_AFHDS2A) { - ui->label_afhds->setText(tr("Options")); - ui->cboAfhdsOpt1->setModel(ModuleData::afhds2aMode1ItemModel()); - ui->cboAfhdsOpt1->setCurrentIndex(Helpers::getBitmappedValue(module.flysky.mode, 1)); - - ui->cboAfhdsOpt2->setModel(ModuleData::afhds2aMode2ItemModel()); - ui->cboAfhdsOpt2->setCurrentIndex(Helpers::getBitmappedValue(module.flysky.mode, 0)); - } - else { - ui->label_afhds->setText(tr("Type")); - ui->cboAfhdsOpt1->setModel(ModuleData::afhds3PhyModeItemModel()); - ui->cboAfhdsOpt1->setCurrentIndex(ui->cboAfhdsOpt1->findData(module.afhds3.phyMode)); - - ui->cboAfhdsOpt2->setModel(ModuleData::afhds3EmiItemModel()); - ui->cboAfhdsOpt2->setCurrentIndex(ui->cboAfhdsOpt2->findData(module.afhds3.emi)); - } - } - - ui->label_afhds->setVisible(mask & MASK_AFHDS); - ui->cboAfhdsOpt1->setVisible(mask & MASK_AFHDS); - ui->cboAfhdsOpt2->setVisible(mask & MASK_AFHDS); - - // Failsafes - ui->label_failsafeMode->setVisible(mask & MASK_FAILSAFES); - ui->failsafeMode->setVisible(mask & MASK_FAILSAFES); - - if ((mask & MASK_FAILSAFES) && module.failsafeMode == FAILSAFE_CUSTOM) { - if (ui->failsafesGroupBox->isHidden()) { - setupFailsafes(); - ui->failsafesGroupBox->setVisible(true); - } - } - else { - ui->failsafesGroupBox->setVisible(false); - } - - if (mask & MASK_FAILSAFES) { - ui->failsafeMode->setCurrentIndex(module.failsafeMode); - - if (firmware->getCapability(ChannelsName)) { - int chan; - QString name; - QMapIterator i(failsafeGroupsMap); - while (i.hasNext()) { - i.next(); - chan = i.key(); - name = QString(model->limitData[chan + module.channelsStart].name).trimmed(); - if (name.isEmpty()) - i.value().label->setText(QString::number(chan + 1)); - else - i.value().label->setText(name); - } - } - } - - ui->label_rxFreq->setVisible((mask & MASK_RX_FREQ)); - ui->rxFreq->setVisible((mask & MASK_RX_FREQ)); - - lock = false; -} - -void ModulePanel::onProtocolChanged(int index) -{ - if (!lock) { - module.channelsCount = module.getMaxChannelCount(); - update(); - - if (module.protocol == PULSES_GHOST || - module.protocol == PULSES_CROSSFIRE) { - if (Boards::getCapability(getCurrentFirmware()->getBoard(), - Board::SportMaxBaudRate) < 400000) { - // default to 115k - ui->telemetryBaudrate->setCurrentIndex(0); - } else { - // default to 400k - ui->telemetryBaudrate->setCurrentIndex(1); - } - } - else if (module.protocol == PULSES_FLYSKY_AFHDS2A) { - module.flysky.setDefault(); - } - else if (module.protocol == PULSES_FLYSKY_AFHDS3) { - module.afhds3.setDefault(); - } - - emit protocolChanged(); - emit updateItemModels(); - emit modified(); - } -} - -void ModulePanel::on_ppmPolarity_currentIndexChanged(int index) -{ - if (!lock && module.ppm.pulsePol != (bool)index) { - module.ppm.pulsePol = index; - emit modified(); - } -} - -void ModulePanel::on_r9mPower_currentIndexChanged(int index) -{ - if (!lock) { - - if (module.protocol == PULSES_FLYSKY_AFHDS2A && module.flysky.rfPower != (unsigned int)index) { - module.flysky.rfPower = index; - emit modified(); - } - else if (module.protocol == PULSES_FLYSKY_AFHDS3 && module.afhds3.rfPower != (unsigned int)index) { - module.afhds3.rfPower = index; - emit modified(); - } - else if(module.pxx.power != (unsigned int)index) - module.pxx.power = index; - } -} - -void ModulePanel::on_ppmOutputType_currentIndexChanged(int index) -{ - if (!lock && module.ppm.outputType != (bool)index) { - module.ppm.outputType = index; - emit modified(); - } -} - -void ModulePanel::on_channelsCount_editingFinished() -{ - if (!lock && module.channelsCount != ui->channelsCount->value()) { - module.channelsCount = ui->channelsCount->value(); - emit channelsRangeChanged(); - update(); - emit modified(); - } -} - -void ModulePanel::on_channelsStart_editingFinished() -{ - if (!lock && module.channelsStart != (unsigned)ui->channelsStart->value() - 1) { - module.channelsStart = (unsigned)ui->channelsStart->value() - 1; - emit channelsRangeChanged(); - update(); - emit modified(); - } -} - -void ModulePanel::on_ppmDelay_editingFinished() -{ - if (!lock && module.ppm.delay != ui->ppmDelay->value()) { - // TODO only accept valid values - module.ppm.delay = ui->ppmDelay->value(); - emit modified(); - } -} - -void ModulePanel::on_rxNumber_editingFinished() -{ - if (module.modelId != (unsigned)ui->rxNumber->value()) { - module.modelId = (unsigned)ui->rxNumber->value(); - emit modified(); - } -} - -void ModulePanel::on_ppmFrameLength_editingFinished() -{ - int val = (ui->ppmFrameLength->value() - 22.5) / 0.5; - if (module.ppm.frameLength != val) { - module.ppm.frameLength = val; - emit modified(); - } -} - -void ModulePanel::on_failsafeMode_currentIndexChanged(int value) -{ - if (!lock && module.failsafeMode != (unsigned)value) { - module.failsafeMode = value; - update(); - emit modified(); - } -} - -void ModulePanel::onMultiProtocolChanged(int index) -{ - int rfProtocol = ui->multiProtocol->itemData(index).toInt(); - if (!lock && module.multi.rfProtocol != (unsigned)rfProtocol) { - lock=true; - module.multi.rfProtocol = (unsigned int)rfProtocol; - module.multi.optionValue = 0; - unsigned int maxSubTypes = multiProtocols.getProtocol(index).numSubTypes(); - if (rfProtocol > MODULE_SUBTYPE_MULTI_LAST) - maxSubTypes = 8; - module.subType = std::min(module.subType, maxSubTypes - 1); - module.channelsCount = module.getMaxChannelCount(); - update(); - emit updateItemModels(); - emit modified(); - lock = false; - } -} - -void ModulePanel::on_optionValue_valueChanged(int value) -{ - if (!lock) { - if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { - module.multi.optionValue = (value - 50) / 5; - } - else { - module.multi.optionValue = value; - } - emit modified(); - } -} - -void ModulePanel::on_chkOption_stateChanged(int state) -{ - if (!lock) { - if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) { - unsigned int opt = (unsigned int)module.multi.optionValue; - if (Helpers::getBitmappedValue(opt, 0) != (state == Qt::Checked)) { - Helpers::setBitmappedValue(opt, (state == Qt::Checked), 0); - module.multi.optionValue = opt; - emit modified(); - } - } - else if (module.multi.optionValue != (state == Qt::Checked)) { - module.multi.optionValue = (state == Qt::Checked); - emit modified(); - } - } -} - -void ModulePanel::on_cboOption_currentIndexChanged(int value) -{ - if (!lock && value >= 0) { - if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) { - unsigned int opt = (unsigned int)module.multi.optionValue; - if (Helpers::getBitmappedValue(opt, 1) != (unsigned int)value) { - Helpers::setBitmappedValue(opt, value, 1); - module.multi.optionValue = opt; - emit modified(); - } - } - else if (module.multi.optionValue != value) { - module.multi.optionValue = value; - emit modified(); - } - } -} - -void ModulePanel::onSubTypeChanged() -{ - const unsigned type = ui->multiSubType->currentData().toUInt(); - if (!lock && module.subType != type) { - lock=true; - module.subType = type; - emit modified(); - lock = false; - } -} - -void ModulePanel::onRfFreqChanged(int freq) { - // TODO fix for AFHDS2A - //if (module.afhds3.rxFreq != (unsigned int)freq) { - // module.afhds3.rxFreq = (unsigned int)freq; - // emit modified(); - //} -} - -void ModulePanel::on_disableTelem_stateChanged(int state) -{ - module.multi.disableTelemetry = (state == Qt::Checked); -} - -void ModulePanel::on_disableChMap_stateChanged(int state) -{ - module.multi.disableMapping = (state == Qt::Checked); -} - -void ModulePanel::on_raw12bits_stateChanged(int state) -{ - module.ghost.raw12bits = (state == Qt::Checked); -} - -void ModulePanel::on_racingMode_stateChanged(int state) -{ - module.access.racingMode = (state == Qt::Checked); -} - -void ModulePanel::on_autoBind_stateChanged(int state) -{ - module.multi.autoBindMode = (state == Qt::Checked); -} - -void ModulePanel::on_lowPower_stateChanged(int state) -{ - module.multi.lowPowerMode = (state == Qt::Checked); -} - -void ModulePanel::onFailsafeModified(unsigned channel) -{ - updateFailsafeUI(channel, FAILSAFE_DISPLAY_PERCENT | FAILSAFE_DISPLAY_USEC); -} - -void ModulePanel::updateFailsafeUI(unsigned channel, quint8 updtSb) -{ - int value = model->limitData[channel].failsafe; - double pctVal = divRoundClosest(value * 1000, 1024) / 10.0; - - if (failsafeGroupsMap.contains(channel)) { - const ChannelFailsafeWidgetsGroup & grp = failsafeGroupsMap.value(channel); - bool disable = (value == FAILSAFE_CHANNEL_HOLD || value == FAILSAFE_CHANNEL_NOPULSE); - if (grp.combo) { - grp.combo->setCurrentIndex(grp.combo->findData(disable ? value : 0)); - } - if ((updtSb & FAILSAFE_DISPLAY_PERCENT) && grp.sbPercent) { - grp.sbPercent->blockSignals(true); - grp.sbPercent->setValue(pctVal); - grp.sbPercent->blockSignals(false); - } - if ((updtSb & FAILSAFE_DISPLAY_USEC) && grp.sbUsec) { - grp.sbUsec->blockSignals(true); - grp.sbUsec->setValue(value); - grp.sbUsec->blockSignals(false); - } - } -} - -// updtSb (update spin box(es)): 0=none or bitmask of FailsafeValueDisplayTypes -void ModulePanel::setChannelFailsafeValue(const int channel, const int value, quint8 updtSb) -{ - if (channel < 0 || channel >= CPN_MAX_CHNOUT) - return; - - model->limitData[channel].failsafe = value; - updateFailsafeUI(channel, updtSb); - - if (!lock) { - emit failsafeModified(channel); - emit modified(); - } -} - -void ModulePanel::onFailsafeUsecChanged(int value) -{ - if (!sender()) - return; - - bool ok = false; - int channel = sender()->property("index").toInt(&ok); - if (ok) - setChannelFailsafeValue(channel, value, FAILSAFE_DISPLAY_PERCENT); -} - -void ModulePanel::onFailsafePercentChanged(double value) -{ - if (!sender()) - return; - - bool ok = false; - int channel = sender()->property("index").toInt(&ok); - if (ok) - setChannelFailsafeValue(channel, divRoundClosest(int(value * 1024), 100), FAILSAFE_DISPLAY_USEC); -} - -void ModulePanel::onFailsafeComboIndexChanged(int index) -{ - if (!sender()) - return; - - QComboBox * cb = qobject_cast(sender()); - - if (cb && !lock) { - lock = true; - bool ok = false; - int channel = sender()->property("index").toInt(&ok); - if (ok) { - model->limitData[channel].failsafe = cb->itemData(index).toInt(); - updateFailsafe(channel); - emit failsafeModified(channel); - emit modified(); - } - lock = false; - } -} - -void ModulePanel::onFailsafesDisplayValueTypeChanged(int type) -{ - if (failsafesValueDisplayType != type) { - failsafesValueDisplayType = type; - foreach (ChannelFailsafeWidgetsGroup grp, failsafeGroupsMap) { - if (grp.sbPercent) - grp.sbPercent->setVisible(type == FAILSAFE_DISPLAY_PERCENT); - if (grp.sbUsec) - grp.sbUsec->setVisible(type == FAILSAFE_DISPLAY_USEC); - } - } -} - -void ModulePanel::onExtendedLimitsToggled() -{ - double channelMaxPct = double(model->getChannelsMax()); - int channelMaxUs = 512 * channelMaxPct / 100 * 2; - foreach (ChannelFailsafeWidgetsGroup grp, failsafeGroupsMap) { - if (grp.sbPercent) - grp.sbPercent->setRange(-channelMaxPct, channelMaxPct); - if (grp.sbUsec) - grp.sbUsec->setRange(-channelMaxUs, channelMaxUs); - } -} - -void ModulePanel::updateFailsafe(unsigned channel) -{ - if (channel >= CPN_MAX_CHNOUT || !failsafeGroupsMap.contains(channel)) - return; - - const int failsafeValue = model->limitData[channel].failsafe; - const ChannelFailsafeWidgetsGroup & grp = failsafeGroupsMap.value(channel); - const bool valDisable = (failsafeValue == FAILSAFE_CHANNEL_HOLD || failsafeValue == FAILSAFE_CHANNEL_NOPULSE); - - if (grp.combo) - grp.combo->setCurrentIndex(grp.combo->findData(valDisable ? failsafeValue : 0)); - if (grp.sbPercent) - grp.sbPercent->setDisabled(valDisable); - if (grp.sbUsec) - grp.sbUsec->setDisabled(valDisable); - - if (!valDisable) - setChannelFailsafeValue(channel, failsafeValue, FAILSAFE_DISPLAY_PERCENT | FAILSAFE_DISPLAY_USEC); -} - -void ModulePanel::onClearAccessRxClicked() -{ - QPushButton *button = qobject_cast(sender()); - - if (button == ui->clearRx1) { - module.access.receivers &= ~(1 << 0); - ui->rx1->clear(); - update(); - emit modified(); - } - else if (button == ui->clearRx2) { - module.access.receivers &= ~(1 << 1); - ui->rx2->clear(); - update(); - emit modified(); - } - else if (button == ui->clearRx3) { - module.access.receivers &= ~(1 << 2); - ui->rx3->clear(); - update(); - emit modified(); - } -} - -void ModulePanel::updateTrainerModeItemModel() -{ - if (isTrainerModule(moduleIdx)) { - if (trainerModeItemModel) - delete trainerModeItemModel; - - trainerModeItemModel = new FilteredItemModel(model->trainerModeItemModel(generalSettings, firmware)); - ui->trainerMode->setModel(trainerModeItemModel); - ui->trainerMode->updateValue(); - } -} - -/******************************************************************************/ - -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 ((int)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); - - fsConfig = ModelData::funcSwitchConfigItemModel(); - AbstractStaticItemModel *fsStart = ModelData::funcSwitchStartItemModel(); - AbstractStaticItemModel *fsGroups = ModelData::funcSwitchGroupsModel(); - - Board::Type board = firmware->getBoard(); - - lock = true; - - 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)); - - AutoLineEdit * aleName = new AutoLineEdit(this); - aleName->setProperty("index", i); - aleName->setValidator(new NameValidator(board, this)); - aleName->setField((char *)model.functionSwitchNames[i], 3); - - QComboBox * cboConfig = new QComboBox(this); - cboConfig->setProperty("index", i); - auto configFilter = new FilteredSwitchConfigsModel(fsConfig, i, &model); - cboConfig->setModel(configFilter); - filterSwitchConfigs << configFilter; - - QComboBox * cboStartPosn = new QComboBox(this); - cboStartPosn->setProperty("index", i); - cboStartPosn->setModel(fsStart); - - 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; - ui->gridSwitches->addWidget(lblSwitchId, row++, i + coloffset); - ui->gridSwitches->addWidget(aleName, row++, i + coloffset); - ui->gridSwitches->addWidget(cboConfig, row++, i + coloffset); - ui->gridSwitches->addWidget(cboStartPosn, 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(cboGroup, QOverload::of(&QComboBox::currentIndexChanged), this, &FunctionSwitchesPanel::on_groupChanged); - - aleNames << aleName; - cboConfigs << cboConfig; - cboStartupPosns << cboStartPosn; - 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(); - - lock = false; -} - -FunctionSwitchesPanel::~FunctionSwitchesPanel() -{ - delete ui; -} - -void FunctionSwitchesPanel::update() -{ - lock = true; - - for (int i = 0; i < switchcnt; i++) { - filterSwitchConfigs[i]->invalidate(); - filterSwitchGroups[i]->invalidate(); - - aleNames[i]->update(); - 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); - cboGroups[i]->setCurrentIndex(grp); - - cboStartupPosns[i]->setEnabled(cboConfigs[i]->currentIndex() >= ModelData::FUNC_SWITCH_CONFIG_2POS && (grp == 0)); - - cboGroups[i]->setEnabled(cboConfigs[i]->currentIndex() >= ModelData::FUNC_SWITCH_CONFIG_TOGGLE); - } - - 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; -} - -void FunctionSwitchesPanel::on_nameEditingFinished() - { - emit updateDataModels(); - } - - -void FunctionSwitchesPanel::on_configCurrentIndexChanged(int index) -{ - if (!sender()) - return; - - QComboBox * cb = qobject_cast(sender()); - - if (cb && !lock) { - lock = true; - bool ok = false; - 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(); - } - lock = false; - } -} - -void FunctionSwitchesPanel::on_startPosnCurrentIndexChanged(int index) -{ - if (!sender()) - return; - - QComboBox * cb = qobject_cast(sender()); - - if (cb && !lock) { - lock = true; - bool ok = false; - 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; - } -} - -void FunctionSwitchesPanel::on_groupChanged(int value) -{ - if (!sender()) - return; - - QComboBox * cb = qobject_cast(sender()); - - if (cb && !lock) { - lock = true; - bool ok = false; - 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(); - emit updateDataModels(); - } - lock = false; - } -} - -void FunctionSwitchesPanel::on_alwaysOnGroupChanged(int value) -{ - if (!sender()) - return; - - QCheckBox * cb = qobject_cast(sender()); - - if (cb && !lock) { - lock = true; - bool ok = false; - int grp = sender()->property("index").toInt(&ok) + 1; - - if (ok) { - model->setFuncSwitchAlwaysOnGroup(grp, (unsigned int)value); - if (value) { - for (int i = 0; i < switchcnt; i += 1) { - if (((int)model->getFuncSwitchGroup(i) == grp) && (model->getFuncSwitchConfig(i) == ModelData::FUNC_SWITCH_CONFIG_TOGGLE)) - model->setFuncSwitchConfig(i, ModelData::FUNC_SWITCH_CONFIG_2POS); - } - if ((int)model->getFuncGroupSwitchStart(grp, switchcnt) == switchcnt + 1) { - for (int i = 0; i < switchcnt; i += 1) { - if ((int)model->getFuncSwitchGroup(i) == grp) - model->setFuncSwitchStart(i, ModelData::FUNC_SWITCH_START_PREVIOUS); - } - } - } - update(); - emit modified(); - } - - lock = false; - } -} - -/******************************************************************************/ +constexpr char FIM_THRSOURCE[] {"Throttle Source"}; +// constexpr char FIM_TRAINERMODE[] {"Trainer Mode"}; +constexpr char FIM_HATSMODE[] {"Hats Mode"}; SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels) : diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index a43ffa46b..4407e139c 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -23,261 +23,16 @@ #include "modeledit.h" #include "eeprominterface.h" -#include "compounditemmodels.h" #include "filtereditemmodels.h" - -#include - -constexpr char MIMETYPE_TIMER[] = "application/x-companion-timer"; +#include "setup_timer.h" +#include "setup_module.h" +#include "setup_function_switches.h" namespace Ui { class Setup; - class Timer; - class Module; - class FunctionSwitches; } class AutoLineEdit; -class FilteredItemModel; - -class TimerPanel : public ModelPanel -{ - Q_OBJECT - - public: - TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, - QWidget * prevFocus, FilteredItemModelFactory * panelFilteredModels, CompoundItemModelFactory * panelItemModels); - virtual ~TimerPanel(); - - virtual void update(); - QWidget * getLastFocus(); - - private slots: - void onNameChanged(); - void onItemModelAboutToBeUpdated(); - void onItemModelUpdateComplete(); - void onCountdownBeepChanged(int index); - void onModeChanged(int index); - - signals: - void nameChanged(); - void modeChanged(); - - private: - TimerData & timer; - Ui::Timer * ui; - void connectItemModelEvents(const FilteredItemModel * itemModel); - int modelsUpdateCnt; -}; - -class ModulePanel : public ModelPanel -{ - Q_OBJECT - - public: - ModulePanel(QWidget * parent, ModelData & model, ModuleData & module, GeneralSettings & generalSettings, Firmware * firmware, int moduleIdx, - FilteredItemModelFactory * panelFilteredItemModels = nullptr, CompoundItemModelFactory * panelItemModels = nullptr); - virtual ~ModulePanel(); - virtual void update(); - - public slots: - void onExtendedLimitsToggled(); - void onFailsafeModified(unsigned index); - void updateTrainerModeItemModel(); - - signals: - void channelsRangeChanged(); - void failsafeModified(unsigned index); - void updateItemModels(); - void protocolChanged(); - - private slots: - void setupFailsafes(); - void onProtocolChanged(int index); - void on_ppmDelay_editingFinished(); - void on_channelsCount_editingFinished(); - void on_channelsStart_editingFinished(); - void on_ppmPolarity_currentIndexChanged(int index); - void on_ppmOutputType_currentIndexChanged(int index); - void on_ppmFrameLength_editingFinished(); - void on_rxNumber_editingFinished(); - void on_failsafeMode_currentIndexChanged(int value); - void onMultiProtocolChanged(int index); - void onSubTypeChanged(); - void on_autoBind_stateChanged(int state); - void on_disableChMap_stateChanged(int state); - void on_raw12bits_stateChanged(int state); - void on_racingMode_stateChanged(int state); - void on_disableTelem_stateChanged(int state); - void on_lowPower_stateChanged(int state); - void on_r9mPower_currentIndexChanged(int index); - void setChannelFailsafeValue(const int channel, const int value, quint8 updtSb = 0); - void onFailsafeComboIndexChanged(int index); - void onFailsafeUsecChanged(int value); - void onFailsafePercentChanged(double value); - void onFailsafesDisplayValueTypeChanged(int type); - void onRfFreqChanged(int freq); - void updateFailsafe(unsigned channel); - void on_optionValue_valueChanged(int value); - void onClearAccessRxClicked(); - void on_chkOption_stateChanged(int state); - void on_cboOption_currentIndexChanged(int value); - - private: - enum FailsafeValueDisplayTypes { FAILSAFE_DISPLAY_PERCENT = 1, FAILSAFE_DISPLAY_USEC = 2 }; - - struct ChannelFailsafeWidgetsGroup { - QLabel * label; - QComboBox * combo; - QSpinBox * sbUsec; - QDoubleSpinBox * sbPercent; - }; - - ModuleData & module; - int moduleIdx; - Ui::Module *ui; - QMap failsafeGroupsMap; - static quint8 failsafesValueDisplayType; // FailsafeValueDisplayTypes - void updateFailsafeUI(unsigned channel, quint8 updtSb); - FilteredItemModel *trainerModeItemModel; - static bool isTrainerModule(int index) { return index < 0; } - static bool isInternalModule(int index) { return index == 0; } - 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 - - public: - FunctionSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware); - virtual ~FunctionSwitchesPanel(); - - void update(); - - signals: - void updateDataModels(); - - private slots: - 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); - - private: - Ui::FunctionSwitches * ui; - QVector aleNames; - QVector cboConfigs; - QVector cboStartupPosns; - 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.cpp b/companion/src/modeledit/setup_function_switches.cpp new file mode 100644 index 000000000..c53a0a3bd --- /dev/null +++ b/companion/src/modeledit/setup_function_switches.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * 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 "setup_function_switches.h" +#include "ui_setup_function_switches.h" +#include "namevalidator.h" +#include "autolineedit.h" + +#include + +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 ((int)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); + + fsConfig = ModelData::funcSwitchConfigItemModel(); + AbstractStaticItemModel *fsStart = ModelData::funcSwitchStartItemModel(); + AbstractStaticItemModel *fsGroups = ModelData::funcSwitchGroupsModel(); + + Board::Type board = firmware->getBoard(); + + lock = true; + + 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)); + + AutoLineEdit * aleName = new AutoLineEdit(this); + aleName->setProperty("index", i); + aleName->setValidator(new NameValidator(board, this)); + aleName->setField((char *)model.functionSwitchNames[i], 3); + + QComboBox * cboConfig = new QComboBox(this); + cboConfig->setProperty("index", i); + auto configFilter = new FilteredSwitchConfigsModel(fsConfig, i, &model); + cboConfig->setModel(configFilter); + filterSwitchConfigs << configFilter; + + QComboBox * cboStartPosn = new QComboBox(this); + cboStartPosn->setProperty("index", i); + cboStartPosn->setModel(fsStart); + + 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; + ui->gridSwitches->addWidget(lblSwitchId, row++, i + coloffset); + ui->gridSwitches->addWidget(aleName, row++, i + coloffset); + ui->gridSwitches->addWidget(cboConfig, row++, i + coloffset); + ui->gridSwitches->addWidget(cboStartPosn, 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(cboGroup, QOverload::of(&QComboBox::currentIndexChanged), this, &FunctionSwitchesPanel::on_groupChanged); + + aleNames << aleName; + cboConfigs << cboConfig; + cboStartupPosns << cboStartPosn; + 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(); + + lock = false; +} + +FunctionSwitchesPanel::~FunctionSwitchesPanel() +{ + delete ui; +} + +void FunctionSwitchesPanel::update() +{ + lock = true; + + for (int i = 0; i < switchcnt; i++) { + filterSwitchConfigs[i]->invalidate(); + filterSwitchGroups[i]->invalidate(); + + aleNames[i]->update(); + 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); + cboGroups[i]->setCurrentIndex(grp); + + cboStartupPosns[i]->setEnabled(cboConfigs[i]->currentIndex() >= ModelData::FUNC_SWITCH_CONFIG_2POS && (grp == 0)); + + cboGroups[i]->setEnabled(cboConfigs[i]->currentIndex() >= ModelData::FUNC_SWITCH_CONFIG_TOGGLE); + } + + 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; +} + +void FunctionSwitchesPanel::on_nameEditingFinished() + { + emit updateDataModels(); + } + + +void FunctionSwitchesPanel::on_configCurrentIndexChanged(int index) +{ + if (!sender()) + return; + + QComboBox * cb = qobject_cast(sender()); + + if (cb && !lock) { + lock = true; + bool ok = false; + 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(); + } + lock = false; + } +} + +void FunctionSwitchesPanel::on_startPosnCurrentIndexChanged(int index) +{ + if (!sender()) + return; + + QComboBox * cb = qobject_cast(sender()); + + if (cb && !lock) { + lock = true; + bool ok = false; + 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; + } +} + +void FunctionSwitchesPanel::on_groupChanged(int value) +{ + if (!sender()) + return; + + QComboBox * cb = qobject_cast(sender()); + + if (cb && !lock) { + lock = true; + bool ok = false; + 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(); + emit updateDataModels(); + } + lock = false; + } +} + +void FunctionSwitchesPanel::on_alwaysOnGroupChanged(int value) +{ + if (!sender()) + return; + + QCheckBox * cb = qobject_cast(sender()); + + if (cb && !lock) { + lock = true; + bool ok = false; + int grp = sender()->property("index").toInt(&ok) + 1; + + if (ok) { + model->setFuncSwitchAlwaysOnGroup(grp, (unsigned int)value); + if (value) { + for (int i = 0; i < switchcnt; i += 1) { + if (((int)model->getFuncSwitchGroup(i) == grp) && (model->getFuncSwitchConfig(i) == ModelData::FUNC_SWITCH_CONFIG_TOGGLE)) + model->setFuncSwitchConfig(i, ModelData::FUNC_SWITCH_CONFIG_2POS); + } + if ((int)model->getFuncGroupSwitchStart(grp, switchcnt) == switchcnt + 1) { + for (int i = 0; i < switchcnt; i += 1) { + if ((int)model->getFuncSwitchGroup(i) == grp) + model->setFuncSwitchStart(i, ModelData::FUNC_SWITCH_START_PREVIOUS); + } + } + } + update(); + emit modified(); + } + + lock = false; + } +} diff --git a/companion/src/modeledit/setup_function_switches.h b/companion/src/modeledit/setup_function_switches.h new file mode 100644 index 000000000..3bf1ff687 --- /dev/null +++ b/companion/src/modeledit/setup_function_switches.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "modeledit.h" +#include "eeprominterface.h" +#include "filtereditemmodels.h" + +class AutoLineEdit; + +namespace Ui { + class FunctionSwitches; +} + +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 + + public: + FunctionSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware); + virtual ~FunctionSwitchesPanel(); + + void update(); + + signals: + void updateDataModels(); + + private slots: + 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); + + private: + Ui::FunctionSwitches * ui; + QVector aleNames; + QVector cboConfigs; + QVector cboStartupPosns; + QVector cboGroups; + QVector cbAlwaysOnGroups; + QVector cboGroupStartupPosns; + QVector filterGroupSwitches; + QVector filterSwitchGroups; + QVector filterSwitchConfigs; + int switchcnt; + AbstractStaticItemModel *fsConfig; + AbstractStaticItemModel *fsGroupStart; +}; diff --git a/companion/src/modeledit/setup_module.cpp b/companion/src/modeledit/setup_module.cpp new file mode 100644 index 000000000..558da90e3 --- /dev/null +++ b/companion/src/modeledit/setup_module.cpp @@ -0,0 +1,1099 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * 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 "setup_module.h" +#include "ui_setup_module.h" +#include "namevalidator.h" +#include "autolineedit.h" +#include "multiprotocols.h" +#include "helpers.h" + +#define FAILSAFE_CHANNEL_HOLD 2000 +#define FAILSAFE_CHANNEL_NOPULSE 2001 + +#define MASK_PROTOCOL (1<<0) +#define MASK_CHANNELS_COUNT (1<<1) +#define MASK_RX_NUMBER (1<<2) +#define MASK_CHANNELS_RANGE (1<<3) +#define MASK_PPM_FIELDS (1<<4) +#define MASK_FAILSAFES (1<<5) +#define MASK_OPEN_DRAIN (1<<6) +#define MASK_MULTIMODULE (1<<7) +#define MASK_ANTENNA (1<<8) +#define MASK_MULTIOPTION (1<<9) +#define MASK_R9M (1<<10) +#define MASK_SBUSPPM_FIELDS (1<<11) +#define MASK_SUBTYPES (1<<12) +#define MASK_ACCESS (1<<13) +#define MASK_RX_FREQ (1<<14) +#define MASK_RF_POWER (1<<15) +#define MASK_RF_RACING_MODE (1<<16) +#define MASK_GHOST (1<<17) +#define MASK_BAUDRATE (1<<18) +#define MASK_MULTI_DSM_OPT (1<<19) +#define MASK_CHANNELMAP (1<<20) +#define MASK_MULTI_BAYANG_OPT (1<<21) +#define MASK_AFHDS (1<<22) +#define MASK_CSRF_ARMING_MODE (1<<23) +#define MASK_CSRF_ARMING_TRIGGER (1<<24) + +quint8 ModulePanel::failsafesValueDisplayType = ModulePanel::FAILSAFE_DISPLAY_PERCENT; + +ModulePanel::ModulePanel(QWidget * parent, ModelData & model, ModuleData & module, GeneralSettings & generalSettings, Firmware * firmware, int moduleIdx, + FilteredItemModelFactory * panelFilteredItemModels, CompoundItemModelFactory * panelItemModels): + ModelPanel(parent, model, generalSettings, firmware), + module(module), + moduleIdx(moduleIdx), + ui(new Ui::Module), + trainerModeItemModel(nullptr) +{ + lock = true; + + ui->setupUi(this); + + ui->label_module->setText(ModuleData::indexToString(moduleIdx, firmware)); + if (isTrainerModule(moduleIdx)) { + ui->formLayout_col1->setSpacing(0); + if (!IS_HORUS_OR_TARANIS(firmware->getBoard())) { + ui->label_trainerMode->hide(); + ui->trainerMode->hide(); + } + else { + updateTrainerModeItemModel(); + ui->trainerMode->setField(model.trainerMode); + connect(ui->trainerMode, &AutoComboBox::currentDataChanged, this, [=] () { + update(); + emit updateItemModels(); + emit modified(); + }); + } + } + else { + ui->label_trainerMode->hide(); + ui->trainerMode->hide(); + } + + if (panelFilteredItemModels) { + if (!isTrainerModule(moduleIdx)) { + int id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(ModuleData::protocolItemModel(generalSettings), moduleIdx + 1/*flag cannot be 0*/), QString("Module Protocol %1").arg(moduleIdx)); + panelFilteredItemModels->getItemModel(id)->setSortCaseSensitivity(Qt::CaseInsensitive); + panelFilteredItemModels->getItemModel(id)->sort(0); + ui->protocol->setModel(panelFilteredItemModels->getItemModel(id)); + + if (ui->protocol->findData(module.protocol) < 0) { + const QString moduleIdxDesc = isInternalModule(moduleIdx) ? tr("internal") : tr("external"); + const QString compareDesc = isInternalModule(moduleIdx) ? tr("hardware") : tr("profile"); + const QString intModuleDesc = isInternalModule(moduleIdx) ? ModuleData::typeToString(generalSettings.internalModule) : ""; + QString msg = tr("Warning: The %1 module protocol %2 is incompatible with the %3 %1 module %4 and has been set to OFF!"); + msg = msg.arg(moduleIdxDesc).arg(module.protocolToString(module.protocol)).arg(compareDesc).arg(intModuleDesc); + + QMessageBox *msgBox = new QMessageBox(this); + msgBox->setIcon( QMessageBox::Warning ); + msgBox->setText(msg); + msgBox->addButton( "Ok", QMessageBox::AcceptRole ); + msgBox->setWindowFlag(Qt::WindowStaysOnTopHint); + msgBox->setAttribute(Qt::WA_DeleteOnClose); // delete pointer after close + msgBox->setModal(false); + msgBox->show(); + + module.clear(); + } + + ui->protocol->setField(module.protocol, this); + } + + if (isInternalModule(moduleIdx)) { + int id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(GeneralSettings::antennaModeItemModel(true)), FIM_ANTENNAMODE); + ui->antennaMode->setModel(panelFilteredItemModels->getItemModel(id)); + } + } + + ui->multiProtocol->setModel(Multiprotocols::protocolItemModel()); + + ui->btnGrpValueType->setId(ui->optPercent, FAILSAFE_DISPLAY_PERCENT); + ui->btnGrpValueType->setId(ui->optUs, FAILSAFE_DISPLAY_USEC); + ui->btnGrpValueType->button(failsafesValueDisplayType)->setChecked(true); + + ui->registrationId->setText(model.registrationId); + + if (panelItemModels) { + ui->crsfArmingMode->setModel(panelItemModels->getItemModel(AIM_MODULE_CRSFARMINGMODE)); + ui->crsfArmingTrigger->setModel(panelFilteredItemModels->getItemModel(FIM_CRSFARMSWITCH)); + } + + setupFailsafes(); + + disableMouseScrolling(); + + update(); + + connect(ui->protocol, &AutoComboBox::currentDataChanged, this, &ModulePanel::onProtocolChanged); + connect(ui->multiSubType, static_cast(&QComboBox::currentIndexChanged), this, &ModulePanel::onSubTypeChanged); + connect(ui->multiProtocol, static_cast(&QComboBox::currentIndexChanged), this, &ModulePanel::onMultiProtocolChanged); + connect(this, &ModulePanel::channelsRangeChanged, this, &ModulePanel::setupFailsafes); + connect(ui->btnGrpValueType, static_cast(&QButtonGroup::idClicked), this, &ModulePanel::onFailsafesDisplayValueTypeChanged); + connect(ui->rxFreq, static_cast(&QSpinBox::valueChanged), this, &ModulePanel::onRfFreqChanged); + connect(ui->clearRx1, SIGNAL(clicked()), this, SLOT(onClearAccessRxClicked())); + connect(ui->clearRx2, SIGNAL(clicked()), this, SLOT(onClearAccessRxClicked())); + connect(ui->clearRx3, SIGNAL(clicked()), this, SLOT(onClearAccessRxClicked())); + connect(ui->cboAfhdsOpt1, static_cast(&QComboBox::currentIndexChanged), [=] (int index) + { + if (lock) + return; + + if (this->module.protocol == PULSES_FLYSKY_AFHDS2A) + Helpers::setBitmappedValue(this->module.flysky.mode, ui->cboAfhdsOpt1->currentData().toInt(), 1); + else + this->module.afhds3.phyMode = ui->cboAfhdsOpt1->currentData().toInt(); + + emit modified(); + }); + connect(ui->cboAfhdsOpt2, static_cast(&QComboBox::currentIndexChanged), [=] (int index) + { + if (lock) + return; + + if (this->module.protocol == PULSES_FLYSKY_AFHDS2A) + Helpers::setBitmappedValue(this->module.flysky.mode, ui->cboAfhdsOpt2->currentData().toInt(), 0); + else + this->module.afhds3.emi = ui->cboAfhdsOpt2->currentData().toInt(); + + emit modified(); + }); + + connect(ui->crsfArmingMode, static_cast(&QComboBox::currentIndexChanged), [=] (int index) + { + this->module.crsf.crsfArmingMode = ui->crsfArmingMode->currentData().toInt(); + if (this->module.crsf.crsfArmingMode != ModuleData::CRSF_ARMING_MODE_SWITCH) + this->module.crsf.crsfArmingTrigger = RawSwitch(SWITCH_TYPE_NONE); + update(); + emit modified(); + }); + + connect(ui->crsfArmingTrigger, static_cast(&QComboBox::currentIndexChanged), [=] (int index) + { + this->module.crsf.crsfArmingTrigger = RawSwitch(ui->crsfArmingTrigger->currentData().toInt()); + emit modified(); + }); + + lock = false; +} + +ModulePanel::~ModulePanel() +{ + delete ui; +} + +void ModulePanel::setupFailsafes() +{ + ChannelFailsafeWidgetsGroup grp; + const int start = module.channelsStart; + const int end = start + module.channelsCount; + const bool hasFailsafe = module.hasFailsafes(firmware); + + lock = true; + + QMutableMapIterator i(failsafeGroupsMap); + while (i.hasNext()) { + i.next(); + grp = i.value(); + ui->failsafesLayout->removeWidget(grp.label); + ui->failsafesLayout->removeWidget(grp.combo); + ui->failsafesLayout->removeWidget(grp.sbPercent); + ui->failsafesLayout->removeWidget(grp.sbUsec); + if (i.key() < start || i.key() >= end || !hasFailsafe) { + grp.label->deleteLater(); + grp.combo->deleteLater(); + grp.sbPercent->deleteLater(); + grp.sbUsec->deleteLater(); + i.remove(); + } + } + + if (!hasFailsafe) { + lock = false; + return; + } + + int row = 0; + int col = 0; + int channelMax = model->getChannelsMax(); + int channelMaxUs = 512 * channelMax / 100 * 2; + + for (int i = start; i < end; ++i) { + if (failsafeGroupsMap.contains(i)) { + grp = failsafeGroupsMap.value(i); + } + else { + QLabel * label = new QLabel(this); + label->setProperty("index", i); + label->setText(QString::number(i + 1)); + + QComboBox * combo = new QComboBox(this); + combo->setProperty("index", i); + combo->addItem(tr("Value"), 0); + combo->addItem(tr("Hold"), FAILSAFE_CHANNEL_HOLD); + combo->addItem(tr("No Pulse"), FAILSAFE_CHANNEL_NOPULSE); + + QDoubleSpinBox * sbDbl = new QDoubleSpinBox(this); + sbDbl->setProperty("index", i); + sbDbl->setMinimumSize(QSize(20, 0)); + sbDbl->setRange(-channelMax, channelMax); + sbDbl->setSingleStep(0.1); + sbDbl->setDecimals(1); + + QSpinBox * sbInt = new QSpinBox(this); + sbInt->setProperty("index", i); + sbInt->setMinimumSize(QSize(20, 0)); + sbInt->setRange(-channelMaxUs, channelMaxUs); + sbInt->setSingleStep(1); + + grp = ChannelFailsafeWidgetsGroup(); + grp.combo = combo; + grp.sbPercent = sbDbl; + grp.sbUsec = sbInt; + grp.label = label; + failsafeGroupsMap.insert(i, grp); + + connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(onFailsafeComboIndexChanged(int))); + connect(sbInt, SIGNAL(valueChanged(int)), this, SLOT(onFailsafeUsecChanged(int))); + connect(sbDbl, SIGNAL(valueChanged(double)), this, SLOT(onFailsafePercentChanged(double))); + } + + ui->failsafesLayout->addWidget(grp.label, row, col, Qt::AlignHCenter); + ui->failsafesLayout->addWidget(grp.combo, row + 1, col, Qt::AlignHCenter); + ui->failsafesLayout->addWidget(grp.sbPercent, row + 2, col, Qt::AlignHCenter); + ui->failsafesLayout->addWidget(grp.sbUsec, row + 3, col, Qt::AlignHCenter); + grp.sbPercent->setVisible(failsafesValueDisplayType == FAILSAFE_DISPLAY_PERCENT); + grp.sbUsec->setVisible(failsafesValueDisplayType == FAILSAFE_DISPLAY_USEC); + + updateFailsafe(i); + + if (++col > 7) { + row += 4; + col = 0; + } + } + + lock = false; +} + +void ModulePanel::update() +{ + lock = true; + + const auto protocol = (PulsesProtocol)module.protocol; + const auto board = firmware->getBoard(); + const auto & pdef = multiProtocols.getProtocol(module.multi.rfProtocol); + unsigned int mask = 0; + unsigned int max_rx_num = 63; + + if (!isTrainerModule(moduleIdx)) { + mask |= MASK_PROTOCOL; + switch (protocol) { + case PULSES_PXX_R9M: + mask |= MASK_R9M | MASK_RF_POWER | MASK_SUBTYPES; + case PULSES_ACCESS_R9M: + case PULSES_ACCESS_R9M_LITE: + case PULSES_ACCESS_R9M_LITE_PRO: + case PULSES_ACCESS_ISRM: + case PULSES_ACCST_ISRM_D16: + case PULSES_XJT_LITE_X16: + case PULSES_XJT_LITE_D8: + case PULSES_XJT_LITE_LR12: + case PULSES_PXX_XJT_X16: + case PULSES_PXX_XJT_D8: + case PULSES_PXX_XJT_LR12: + case PULSES_PXX_DJT: + mask |= MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT; + // ACCST Rx ID + if (protocol==PULSES_PXX_XJT_X16 || protocol==PULSES_PXX_XJT_LR12 || + protocol==PULSES_PXX_R9M || protocol==PULSES_ACCST_ISRM_D16 || + protocol==PULSES_XJT_LITE_X16 || protocol==PULSES_XJT_LITE_LR12) + mask |= MASK_RX_NUMBER; + // ACCESS + else if (protocol==PULSES_ACCESS_ISRM || protocol==PULSES_ACCESS_R9M || + protocol==PULSES_ACCESS_R9M_LITE || protocol==PULSES_ACCESS_R9M_LITE_PRO) + mask |= MASK_RX_NUMBER | MASK_ACCESS; + if (isInternalModule(moduleIdx) && + (protocol==PULSES_PXX_XJT_X16 || + protocol==PULSES_PXX_XJT_D8 || protocol==PULSES_PXX_XJT_LR12) && + HAS_EXTERNAL_ANTENNA(board) && generalSettings.antennaMode == GeneralSettings::ANTENNA_MODE_PER_MODEL) + mask |= MASK_ANTENNA; + if (protocol == PULSES_ACCESS_ISRM && module.channelsCount == 8) + mask |= MASK_RF_RACING_MODE; + break; + case PULSES_LP45: + case PULSES_DSM2: + case PULSES_DSMX: + mask |= MASK_CHANNELS_RANGE | MASK_RX_NUMBER; + module.channelsCount = 6; + max_rx_num = 20; + break; + case PULSES_CROSSFIRE: + mask |= MASK_CHANNELS_RANGE | MASK_RX_NUMBER | MASK_BAUDRATE | MASK_CSRF_ARMING_MODE; + module.channelsCount = 16; + ui->telemetryBaudrate->setModel(ModuleData::telemetryBaudrateItemModel(protocol)); + ui->telemetryBaudrate->setField(module.crsf.telemetryBaudrate); + ui->crsfArmingMode->setCurrentIndex(module.crsf.crsfArmingMode); + if (module.crsf.crsfArmingMode == ModuleData::CRSF_ARMING_MODE_SWITCH) { + mask |= MASK_CSRF_ARMING_TRIGGER; + ui->crsfArmingTrigger->setCurrentIndex(ui->crsfArmingTrigger->findData(RawSwitch(module.crsf.crsfArmingTrigger).toValue())); + } + break; + case PULSES_GHOST: + mask |= MASK_CHANNELS_RANGE | MASK_GHOST | MASK_BAUDRATE; + module.channelsCount = 16; + ui->telemetryBaudrate->setModel(ModuleData::telemetryBaudrateItemModel(protocol)); + ui->telemetryBaudrate->setField(module.ghost.telemetryBaudrate); + break; + case PULSES_PPM: + mask |= MASK_SUBTYPES | MASK_PPM_FIELDS | MASK_SBUSPPM_FIELDS| MASK_CHANNELS_RANGE| MASK_CHANNELS_COUNT; + if (IS_9XRPRO(board)) { + mask |= MASK_OPEN_DRAIN; + } + break; + case PULSES_SBUS: + module.channelsCount = 16; + mask |= MASK_SBUSPPM_FIELDS| MASK_CHANNELS_RANGE; + break; + case PULSES_MULTIMODULE: + mask |= MASK_CHANNELS_RANGE | MASK_RX_NUMBER | MASK_MULTIMODULE | MASK_SUBTYPES; + + switch (module.multi.rfProtocol) { + case MODULE_SUBTYPE_MULTI_OLRS: + max_rx_num = MODULE_SUBTYPE_MULTI_OLRS_RXNUM; + case MODULE_SUBTYPE_MULTI_BUGS: + max_rx_num = MODULE_SUBTYPE_MULTI_BUGS_RXNUM; + case MODULE_SUBTYPE_MULTI_BUGS_MINI: + max_rx_num = MODULE_SUBTYPE_MULTI_BUGS_MINI_RXNUM; + default: + max_rx_num = 63; + } + + if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) + mask |= MASK_CHANNELS_COUNT; + else + module.channelsCount = 16; + if (pdef.optionsstr != nullptr) { + if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) + mask |= MASK_MULTI_DSM_OPT; + else if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_BAYANG) + mask |= MASK_MULTI_BAYANG_OPT; + else + mask |= MASK_MULTIOPTION; + } + if (pdef.hasFailsafe || (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_FRSKY && (module.subType == 0 || module.subType == 2 || module.subType > 3 ))) + mask |= MASK_FAILSAFES; + if (pdef.disableChannelMap) + mask |= MASK_CHANNELMAP; + break; + case PULSES_FLYSKY_AFHDS3: + mask |= MASK_RX_NUMBER; + case PULSES_FLYSKY_AFHDS2A: + mask |= MASK_CHANNELS_RANGE| MASK_CHANNELS_COUNT | MASK_FAILSAFES | MASK_AFHDS; + break; + case PULSES_LEMON_DSMP: + mask |= MASK_CHANNELS_RANGE; + break; + default: + break; + } + + if (protocol != PULSES_MULTIMODULE && module.hasFailsafes(firmware)) + mask |= MASK_FAILSAFES; + } + else if (IS_HORUS_OR_TARANIS(board)) { + if (model->trainerMode == TRAINER_MODE_SLAVE_JACK) { + mask |= MASK_PPM_FIELDS | MASK_SBUSPPM_FIELDS | MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT; + } + } + else if (model->trainerMode != TRAINER_MODE_MASTER_JACK) { + mask |= MASK_PPM_FIELDS | MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT; + } + + if (isExternalModule(moduleIdx)) + ui->telemetryBaudrate->setVisible(mask & MASK_BAUDRATE); + else + ui->telemetryBaudrate->setVisible(false); + + ui->label_protocol->setVisible(mask & MASK_PROTOCOL); + ui->protocol->setVisible(mask & MASK_PROTOCOL); + ui->label_rxNumber->setVisible(mask & MASK_RX_NUMBER); + ui->rxNumber->setVisible(mask & MASK_RX_NUMBER); + ui->rxNumber->setMaximum(max_rx_num); + ui->rxNumber->setValue(module.modelId); + ui->label_channelsStart->setVisible(mask & MASK_CHANNELS_RANGE); + ui->channelsStart->setVisible(mask & MASK_CHANNELS_RANGE); + ui->channelsStart->setMaximum(33 - module.channelsCount); + ui->channelsStart->setValue(module.channelsStart+1); + ui->label_channelsCount->setVisible(mask & MASK_CHANNELS_RANGE); + ui->channelsCount->setVisible(mask & MASK_CHANNELS_RANGE); + ui->channelsCount->setEnabled(mask & MASK_CHANNELS_COUNT); + ui->channelsCount->setMaximum(module.getMaxChannelCount()); + ui->channelsCount->setValue(module.channelsCount); + ui->channelsCount->setSingleStep(firmware->getCapability(HasPPMStart) ? 1 : 2); + ui->label_crsfArmingMode->setVisible(mask & MASK_CSRF_ARMING_MODE); + ui->crsfArmingMode->setVisible(mask & MASK_CSRF_ARMING_MODE); + ui->crsfArmingTrigger->setVisible(mask & MASK_CSRF_ARMING_TRIGGER); + + // PPM settings fields + ui->label_ppmPolarity->setVisible(mask & MASK_SBUSPPM_FIELDS); + ui->ppmPolarity->setVisible(mask & MASK_SBUSPPM_FIELDS); + ui->ppmPolarity->setCurrentIndex(module.ppm.pulsePol); + ui->label_ppmOutputType->setVisible(mask & MASK_OPEN_DRAIN); + ui->ppmOutputType->setVisible(mask & MASK_OPEN_DRAIN); + ui->ppmOutputType->setCurrentIndex(module.ppm.outputType); + ui->label_ppmDelay->setVisible(mask & MASK_PPM_FIELDS); + ui->ppmDelay->setVisible(mask & MASK_PPM_FIELDS); + ui->ppmDelay->setValue(module.ppm.delay); + ui->label_ppmFrameLength->setVisible(mask & MASK_SBUSPPM_FIELDS); + ui->ppmFrameLength->setVisible(mask & MASK_SBUSPPM_FIELDS); + ui->ppmFrameLength->setMinimum(module.channelsCount * (model->extendedLimits ? 2.250 : 2)+3.5); + ui->ppmFrameLength->setMaximum(firmware->getCapability(PPMFrameLength)); + ui->ppmFrameLength->setValue(22.5 + ((double)module.ppm.frameLength) * 0.5); + + if (mask & MASK_ANTENNA) { + if (module.pxx.antennaMode == GeneralSettings::ANTENNA_MODE_PER_MODEL) + module.pxx.antennaMode = GeneralSettings::ANTENNA_MODE_INTERNAL; + ui->antennaMode->setField(module.pxx.antennaMode, this); + ui->antennaLabel->show(); + ui->antennaMode->show(); + } + else { + ui->antennaLabel->hide(); + ui->antennaMode->hide(); + } + + if (mask & MASK_RF_RACING_MODE) { + ui->racingMode->show(); + ui->racingMode->setChecked(module.access.racingMode); + } + else { + ui->racingMode->hide(); + } + + // R9M options + ui->r9mPower->setVisible(mask & MASK_RF_POWER); + ui->label_r9mPower->setVisible(mask & MASK_RF_POWER); + ui->warning_r9mPower->setVisible((mask & MASK_R9M) && module.subType == MODULE_SUBTYPE_R9M_EU); + ui->warning_r9mFlex->setVisible((mask & MASK_R9M) && module.subType > MODULE_SUBTYPE_R9M_EU); + + if (mask & MASK_RF_POWER) { + const QSignalBlocker blocker(ui->r9mPower); + ui->r9mPower->clear(); + ui->r9mPower->addItems(ModuleData::powerValueStrings(protocol, module.subType, firmware)); + ui->r9mPower->setCurrentIndex(mask & MASK_R9M ? module.pxx.power : module.afhds3.rfPower); + } + + // module subtype + ui->label_multiSubType->setVisible(mask & MASK_SUBTYPES); + ui->multiSubType->setVisible(mask & MASK_SUBTYPES); + if (mask & MASK_SUBTYPES) { + unsigned numEntries = 2; // R9M FCC/EU + unsigned i = 0; + switch(protocol){ + case PULSES_MULTIMODULE: + numEntries = (module.multi.rfProtocol > MODULE_SUBTYPE_MULTI_LAST ? 8 : pdef.numSubTypes()); + break; + case PULSES_PXX_R9M: + if (firmware->getCapability(HasModuleR9MFlex)) + i = 2; + break; + case PULSES_PPM: + numEntries = PPM_NUM_SUBTYPES; + break; + default: + break; + } + numEntries += i; + const QSignalBlocker blocker(ui->multiSubType); + ui->multiSubType->clear(); + for ( ; i < numEntries; i++) + ui->multiSubType->addItem(module.subTypeToString(i), i); + ui->multiSubType->setCurrentIndex(ui->multiSubType->findData(module.subType)); + } + + // Multi settings fields + ui->label_multiProtocol->setVisible(mask & MASK_MULTIMODULE); + ui->multiProtocol->setVisible(mask & MASK_MULTIMODULE); + ui->label_option->setVisible(mask & MASK_MULTIOPTION); + ui->optionValue->setVisible(mask & MASK_MULTIOPTION); + ui->lblChkOption->setVisible(mask & MASK_MULTI_DSM_OPT); + ui->chkOption->setVisible(mask & MASK_MULTI_DSM_OPT); + ui->lblCboOption->setVisible(mask & MASK_MULTI_DSM_OPT || mask & MASK_MULTI_BAYANG_OPT); + ui->cboOption->setVisible(mask & MASK_MULTI_DSM_OPT || mask & MASK_MULTI_BAYANG_OPT); + ui->disableTelem->setVisible(mask & MASK_MULTIMODULE); + ui->disableChMap->setVisible(mask & MASK_CHANNELMAP); + ui->lowPower->setVisible(mask & MASK_MULTIMODULE); + ui->autoBind->setVisible(mask & MASK_MULTIMODULE); + if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) + ui->autoBind->setVisible(false); + else + ui->autoBind->setText(tr("Bind on channel")); + + if (mask & MASK_MULTIMODULE) { + ui->multiProtocol->setCurrentIndex(ui->multiProtocol->findData(module.multi.rfProtocol)); + ui->disableTelem->setChecked(module.multi.disableTelemetry); + ui->disableChMap->setChecked(module.multi.disableMapping); + ui->autoBind->setChecked(module.multi.autoBindMode); + ui->lowPower->setChecked(module.multi.lowPowerMode); + } + + if (mask & MASK_MULTIOPTION) { + ui->label_option->setText(qApp->translate("Multiprotocols", qPrintable(pdef.optionsstr))); + int8_t min, max; + getMultiOptionValues(module.multi.rfProtocol, min, max); + + auto lineEdit = ui->optionValue->findChild(); + + if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { + ui->optionValue->setMinimum(50 + 5 * min); + ui->optionValue->setMaximum(50 + 5 * max); + ui->optionValue->setSingleStep(5); + ui->optionValue->setValue(50 + 5 * module.multi.optionValue); + if (lineEdit) { + lineEdit->setReadOnly(true); + lineEdit->setFocusPolicy(Qt::NoFocus); + } + } + else { + ui->optionValue->setMinimum(min); + ui->optionValue->setMaximum(max); + ui->optionValue->setSingleStep(1); + ui->optionValue->setValue(module.multi.optionValue); + if (lineEdit) { + lineEdit->setReadOnly(false); + lineEdit->setFocusPolicy(Qt::WheelFocus); + } + } + } + + if (mask & MASK_MULTI_DSM_OPT) { + ui->lblChkOption->setText(qApp->translate("Multiprotocols", qPrintable(pdef.optionsstr))); + ui->chkOption->setChecked(Helpers::getBitmappedValue(module.multi.optionValue, 0)); + ui->lblCboOption->setText(qApp->translate("Multiprotocols", "Servo update rate")); + ui->cboOption->clear(); + ui->cboOption->addItems({ DSM2_OPTION_SERVOFREQ_NAMES }); + ui->cboOption->setSizeAdjustPolicy(QComboBox::AdjustToContents); + ui->cboOption->setCurrentIndex(Helpers::getBitmappedValue(module.multi.optionValue, 1)); + } + + if (mask & MASK_MULTI_BAYANG_OPT) { + ui->lblCboOption->setText(qApp->translate("Multiprotocols", qPrintable(pdef.optionsstr))); + ui->cboOption->clear(); + ui->cboOption->addItems({ BAYANG_OPTION_TELEMETRY_NAMES }); + ui->cboOption->setSizeAdjustPolicy(QComboBox::AdjustToContents); + ui->cboOption->setCurrentIndex(Helpers::getBitmappedValue(module.multi.optionValue, 1)); + } + + // Ghost settings fields + ui->raw12bits->setVisible(mask & MASK_GHOST); + if (mask & MASK_GHOST) { + ui->raw12bits->setChecked(module.ghost.raw12bits); + } + + if (mask & MASK_ACCESS) { + ui->rx1->setText(module.access.receiverName[0]); + ui->rx2->setText(module.access.receiverName[1]); + ui->rx3->setText(module.access.receiverName[2]); + } + + ui->registrationIdLabel->setVisible(mask & MASK_ACCESS); + ui->registrationId->setVisible(mask & MASK_ACCESS); + + ui->rx1Label->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 0))); + ui->clearRx1->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 0))); + ui->rx1->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 0))); + + ui->rx2Label->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 1))); + ui->clearRx2->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 1))); + ui->rx2->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 1))); + + ui->rx3Label->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 2))); + ui->clearRx3->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 2))); + ui->rx3->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 2))); + + // AFHFS + if (mask & MASK_AFHDS) { + if (protocol == PULSES_FLYSKY_AFHDS2A) { + ui->label_afhds->setText(tr("Options")); + ui->cboAfhdsOpt1->setModel(ModuleData::afhds2aMode1ItemModel()); + ui->cboAfhdsOpt1->setCurrentIndex(Helpers::getBitmappedValue(module.flysky.mode, 1)); + + ui->cboAfhdsOpt2->setModel(ModuleData::afhds2aMode2ItemModel()); + ui->cboAfhdsOpt2->setCurrentIndex(Helpers::getBitmappedValue(module.flysky.mode, 0)); + } + else { + ui->label_afhds->setText(tr("Type")); + ui->cboAfhdsOpt1->setModel(ModuleData::afhds3PhyModeItemModel()); + ui->cboAfhdsOpt1->setCurrentIndex(ui->cboAfhdsOpt1->findData(module.afhds3.phyMode)); + + ui->cboAfhdsOpt2->setModel(ModuleData::afhds3EmiItemModel()); + ui->cboAfhdsOpt2->setCurrentIndex(ui->cboAfhdsOpt2->findData(module.afhds3.emi)); + } + } + + ui->label_afhds->setVisible(mask & MASK_AFHDS); + ui->cboAfhdsOpt1->setVisible(mask & MASK_AFHDS); + ui->cboAfhdsOpt2->setVisible(mask & MASK_AFHDS); + + // Failsafes + ui->label_failsafeMode->setVisible(mask & MASK_FAILSAFES); + ui->failsafeMode->setVisible(mask & MASK_FAILSAFES); + + if ((mask & MASK_FAILSAFES) && module.failsafeMode == FAILSAFE_CUSTOM) { + if (ui->failsafesGroupBox->isHidden()) { + setupFailsafes(); + ui->failsafesGroupBox->setVisible(true); + } + } + else { + ui->failsafesGroupBox->setVisible(false); + } + + if (mask & MASK_FAILSAFES) { + ui->failsafeMode->setCurrentIndex(module.failsafeMode); + + if (firmware->getCapability(ChannelsName)) { + int chan; + QString name; + QMapIterator i(failsafeGroupsMap); + while (i.hasNext()) { + i.next(); + chan = i.key(); + name = QString(model->limitData[chan + module.channelsStart].name).trimmed(); + if (name.isEmpty()) + i.value().label->setText(QString::number(chan + 1)); + else + i.value().label->setText(name); + } + } + } + + ui->label_rxFreq->setVisible((mask & MASK_RX_FREQ)); + ui->rxFreq->setVisible((mask & MASK_RX_FREQ)); + + lock = false; +} + +void ModulePanel::onProtocolChanged(int index) +{ + if (!lock) { + module.channelsCount = module.getMaxChannelCount(); + update(); + + if (module.protocol == PULSES_GHOST || + module.protocol == PULSES_CROSSFIRE) { + if (Boards::getCapability(getCurrentFirmware()->getBoard(), + Board::SportMaxBaudRate) < 400000) { + // default to 115k + ui->telemetryBaudrate->setCurrentIndex(0); + } else { + // default to 400k + ui->telemetryBaudrate->setCurrentIndex(1); + } + } + else if (module.protocol == PULSES_FLYSKY_AFHDS2A) { + module.flysky.setDefault(); + } + else if (module.protocol == PULSES_FLYSKY_AFHDS3) { + module.afhds3.setDefault(); + } + + emit protocolChanged(); + emit updateItemModels(); + emit modified(); + } +} + +void ModulePanel::on_ppmPolarity_currentIndexChanged(int index) +{ + if (!lock && module.ppm.pulsePol != (bool)index) { + module.ppm.pulsePol = index; + emit modified(); + } +} + +void ModulePanel::on_r9mPower_currentIndexChanged(int index) +{ + if (!lock) { + + if (module.protocol == PULSES_FLYSKY_AFHDS2A && module.flysky.rfPower != (unsigned int)index) { + module.flysky.rfPower = index; + emit modified(); + } + else if (module.protocol == PULSES_FLYSKY_AFHDS3 && module.afhds3.rfPower != (unsigned int)index) { + module.afhds3.rfPower = index; + emit modified(); + } + else if(module.pxx.power != (unsigned int)index) + module.pxx.power = index; + } +} + +void ModulePanel::on_ppmOutputType_currentIndexChanged(int index) +{ + if (!lock && module.ppm.outputType != (bool)index) { + module.ppm.outputType = index; + emit modified(); + } +} + +void ModulePanel::on_channelsCount_editingFinished() +{ + if (!lock && module.channelsCount != ui->channelsCount->value()) { + module.channelsCount = ui->channelsCount->value(); + emit channelsRangeChanged(); + update(); + emit modified(); + } +} + +void ModulePanel::on_channelsStart_editingFinished() +{ + if (!lock && module.channelsStart != (unsigned)ui->channelsStart->value() - 1) { + module.channelsStart = (unsigned)ui->channelsStart->value() - 1; + emit channelsRangeChanged(); + update(); + emit modified(); + } +} + +void ModulePanel::on_ppmDelay_editingFinished() +{ + if (!lock && module.ppm.delay != ui->ppmDelay->value()) { + // TODO only accept valid values + module.ppm.delay = ui->ppmDelay->value(); + emit modified(); + } +} + +void ModulePanel::on_rxNumber_editingFinished() +{ + if (module.modelId != (unsigned)ui->rxNumber->value()) { + module.modelId = (unsigned)ui->rxNumber->value(); + emit modified(); + } +} + +void ModulePanel::on_ppmFrameLength_editingFinished() +{ + int val = (ui->ppmFrameLength->value() - 22.5) / 0.5; + if (module.ppm.frameLength != val) { + module.ppm.frameLength = val; + emit modified(); + } +} + +void ModulePanel::on_failsafeMode_currentIndexChanged(int value) +{ + if (!lock && module.failsafeMode != (unsigned)value) { + module.failsafeMode = value; + update(); + emit modified(); + } +} + +void ModulePanel::onMultiProtocolChanged(int index) +{ + int rfProtocol = ui->multiProtocol->itemData(index).toInt(); + if (!lock && module.multi.rfProtocol != (unsigned)rfProtocol) { + lock=true; + module.multi.rfProtocol = (unsigned int)rfProtocol; + module.multi.optionValue = 0; + unsigned int maxSubTypes = multiProtocols.getProtocol(index).numSubTypes(); + if (rfProtocol > MODULE_SUBTYPE_MULTI_LAST) + maxSubTypes = 8; + module.subType = std::min(module.subType, maxSubTypes - 1); + module.channelsCount = module.getMaxChannelCount(); + update(); + emit updateItemModels(); + emit modified(); + lock = false; + } +} + +void ModulePanel::on_optionValue_valueChanged(int value) +{ + if (!lock) { + if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { + module.multi.optionValue = (value - 50) / 5; + } + else { + module.multi.optionValue = value; + } + emit modified(); + } +} + +void ModulePanel::on_chkOption_stateChanged(int state) +{ + if (!lock) { + if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) { + unsigned int opt = (unsigned int)module.multi.optionValue; + if (Helpers::getBitmappedValue(opt, 0) != (state == Qt::Checked)) { + Helpers::setBitmappedValue(opt, (state == Qt::Checked), 0); + module.multi.optionValue = opt; + emit modified(); + } + } + else if (module.multi.optionValue != (state == Qt::Checked)) { + module.multi.optionValue = (state == Qt::Checked); + emit modified(); + } + } +} + +void ModulePanel::on_cboOption_currentIndexChanged(int value) +{ + if (!lock && value >= 0) { + if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) { + unsigned int opt = (unsigned int)module.multi.optionValue; + if (Helpers::getBitmappedValue(opt, 1) != (unsigned int)value) { + Helpers::setBitmappedValue(opt, value, 1); + module.multi.optionValue = opt; + emit modified(); + } + } + else if (module.multi.optionValue != value) { + module.multi.optionValue = value; + emit modified(); + } + } +} + +void ModulePanel::onSubTypeChanged() +{ + const unsigned type = ui->multiSubType->currentData().toUInt(); + if (!lock && module.subType != type) { + lock=true; + module.subType = type; + emit modified(); + lock = false; + } +} + +void ModulePanel::onRfFreqChanged(int freq) { + // TODO fix for AFHDS2A + //if (module.afhds3.rxFreq != (unsigned int)freq) { + // module.afhds3.rxFreq = (unsigned int)freq; + // emit modified(); + //} +} + +void ModulePanel::on_disableTelem_stateChanged(int state) +{ + module.multi.disableTelemetry = (state == Qt::Checked); +} + +void ModulePanel::on_disableChMap_stateChanged(int state) +{ + module.multi.disableMapping = (state == Qt::Checked); +} + +void ModulePanel::on_raw12bits_stateChanged(int state) +{ + module.ghost.raw12bits = (state == Qt::Checked); +} + +void ModulePanel::on_racingMode_stateChanged(int state) +{ + module.access.racingMode = (state == Qt::Checked); +} + +void ModulePanel::on_autoBind_stateChanged(int state) +{ + module.multi.autoBindMode = (state == Qt::Checked); +} + +void ModulePanel::on_lowPower_stateChanged(int state) +{ + module.multi.lowPowerMode = (state == Qt::Checked); +} + +void ModulePanel::onFailsafeModified(unsigned channel) +{ + updateFailsafeUI(channel, FAILSAFE_DISPLAY_PERCENT | FAILSAFE_DISPLAY_USEC); +} + +void ModulePanel::updateFailsafeUI(unsigned channel, quint8 updtSb) +{ + int value = model->limitData[channel].failsafe; + double pctVal = divRoundClosest(value * 1000, 1024) / 10.0; + + if (failsafeGroupsMap.contains(channel)) { + const ChannelFailsafeWidgetsGroup & grp = failsafeGroupsMap.value(channel); + bool disable = (value == FAILSAFE_CHANNEL_HOLD || value == FAILSAFE_CHANNEL_NOPULSE); + if (grp.combo) { + grp.combo->setCurrentIndex(grp.combo->findData(disable ? value : 0)); + } + if ((updtSb & FAILSAFE_DISPLAY_PERCENT) && grp.sbPercent) { + grp.sbPercent->blockSignals(true); + grp.sbPercent->setValue(pctVal); + grp.sbPercent->blockSignals(false); + } + if ((updtSb & FAILSAFE_DISPLAY_USEC) && grp.sbUsec) { + grp.sbUsec->blockSignals(true); + grp.sbUsec->setValue(value); + grp.sbUsec->blockSignals(false); + } + } +} + +// updtSb (update spin box(es)): 0=none or bitmask of FailsafeValueDisplayTypes +void ModulePanel::setChannelFailsafeValue(const int channel, const int value, quint8 updtSb) +{ + if (channel < 0 || channel >= CPN_MAX_CHNOUT) + return; + + model->limitData[channel].failsafe = value; + updateFailsafeUI(channel, updtSb); + + if (!lock) { + emit failsafeModified(channel); + emit modified(); + } +} + +void ModulePanel::onFailsafeUsecChanged(int value) +{ + if (!sender()) + return; + + bool ok = false; + int channel = sender()->property("index").toInt(&ok); + if (ok) + setChannelFailsafeValue(channel, value, FAILSAFE_DISPLAY_PERCENT); +} + +void ModulePanel::onFailsafePercentChanged(double value) +{ + if (!sender()) + return; + + bool ok = false; + int channel = sender()->property("index").toInt(&ok); + if (ok) + setChannelFailsafeValue(channel, divRoundClosest(int(value * 1024), 100), FAILSAFE_DISPLAY_USEC); +} + +void ModulePanel::onFailsafeComboIndexChanged(int index) +{ + if (!sender()) + return; + + QComboBox * cb = qobject_cast(sender()); + + if (cb && !lock) { + lock = true; + bool ok = false; + int channel = sender()->property("index").toInt(&ok); + if (ok) { + model->limitData[channel].failsafe = cb->itemData(index).toInt(); + updateFailsafe(channel); + emit failsafeModified(channel); + emit modified(); + } + lock = false; + } +} + +void ModulePanel::onFailsafesDisplayValueTypeChanged(int type) +{ + if (failsafesValueDisplayType != type) { + failsafesValueDisplayType = type; + foreach (ChannelFailsafeWidgetsGroup grp, failsafeGroupsMap) { + if (grp.sbPercent) + grp.sbPercent->setVisible(type == FAILSAFE_DISPLAY_PERCENT); + if (grp.sbUsec) + grp.sbUsec->setVisible(type == FAILSAFE_DISPLAY_USEC); + } + } +} + +void ModulePanel::onExtendedLimitsToggled() +{ + double channelMaxPct = double(model->getChannelsMax()); + int channelMaxUs = 512 * channelMaxPct / 100 * 2; + foreach (ChannelFailsafeWidgetsGroup grp, failsafeGroupsMap) { + if (grp.sbPercent) + grp.sbPercent->setRange(-channelMaxPct, channelMaxPct); + if (grp.sbUsec) + grp.sbUsec->setRange(-channelMaxUs, channelMaxUs); + } +} + +void ModulePanel::updateFailsafe(unsigned channel) +{ + if (channel >= CPN_MAX_CHNOUT || !failsafeGroupsMap.contains(channel)) + return; + + const int failsafeValue = model->limitData[channel].failsafe; + const ChannelFailsafeWidgetsGroup & grp = failsafeGroupsMap.value(channel); + const bool valDisable = (failsafeValue == FAILSAFE_CHANNEL_HOLD || failsafeValue == FAILSAFE_CHANNEL_NOPULSE); + + if (grp.combo) + grp.combo->setCurrentIndex(grp.combo->findData(valDisable ? failsafeValue : 0)); + if (grp.sbPercent) + grp.sbPercent->setDisabled(valDisable); + if (grp.sbUsec) + grp.sbUsec->setDisabled(valDisable); + + if (!valDisable) + setChannelFailsafeValue(channel, failsafeValue, FAILSAFE_DISPLAY_PERCENT | FAILSAFE_DISPLAY_USEC); +} + +void ModulePanel::onClearAccessRxClicked() +{ + QPushButton *button = qobject_cast(sender()); + + if (button == ui->clearRx1) { + module.access.receivers &= ~(1 << 0); + ui->rx1->clear(); + update(); + emit modified(); + } + else if (button == ui->clearRx2) { + module.access.receivers &= ~(1 << 1); + ui->rx2->clear(); + update(); + emit modified(); + } + else if (button == ui->clearRx3) { + module.access.receivers &= ~(1 << 2); + ui->rx3->clear(); + update(); + emit modified(); + } +} + +void ModulePanel::updateTrainerModeItemModel() +{ + if (isTrainerModule(moduleIdx)) { + if (trainerModeItemModel) + delete trainerModeItemModel; + + trainerModeItemModel = new FilteredItemModel(model->trainerModeItemModel(generalSettings, firmware)); + ui->trainerMode->setModel(trainerModeItemModel); + ui->trainerMode->updateValue(); + } +} diff --git a/companion/src/modeledit/setup_module.h b/companion/src/modeledit/setup_module.h new file mode 100644 index 000000000..56415f888 --- /dev/null +++ b/companion/src/modeledit/setup_module.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "modeledit.h" +#include "eeprominterface.h" +#include "filtereditemmodels.h" + +constexpr char FIM_ANTENNAMODE[] {"Antenna Mode"}; +constexpr char FIM_CRSFARMSWITCH[] {"CRSF Arming Switch"}; + +namespace Ui { + class Module; +} + +class ModulePanel : public ModelPanel +{ + Q_OBJECT + + public: + ModulePanel(QWidget * parent, ModelData & model, ModuleData & module, GeneralSettings & generalSettings, Firmware * firmware, int moduleIdx, + FilteredItemModelFactory * panelFilteredItemModels = nullptr, CompoundItemModelFactory * panelItemModels = nullptr); + virtual ~ModulePanel(); + + virtual void update(); + + public slots: + void onExtendedLimitsToggled(); + void onFailsafeModified(unsigned index); + void updateTrainerModeItemModel(); + + signals: + void channelsRangeChanged(); + void failsafeModified(unsigned index); + void updateItemModels(); + void protocolChanged(); + + private slots: + void setupFailsafes(); + void onProtocolChanged(int index); + void on_ppmDelay_editingFinished(); + void on_channelsCount_editingFinished(); + void on_channelsStart_editingFinished(); + void on_ppmPolarity_currentIndexChanged(int index); + void on_ppmOutputType_currentIndexChanged(int index); + void on_ppmFrameLength_editingFinished(); + void on_rxNumber_editingFinished(); + void on_failsafeMode_currentIndexChanged(int value); + void onMultiProtocolChanged(int index); + void onSubTypeChanged(); + void on_autoBind_stateChanged(int state); + void on_disableChMap_stateChanged(int state); + void on_raw12bits_stateChanged(int state); + void on_racingMode_stateChanged(int state); + void on_disableTelem_stateChanged(int state); + void on_lowPower_stateChanged(int state); + void on_r9mPower_currentIndexChanged(int index); + void setChannelFailsafeValue(const int channel, const int value, quint8 updtSb = 0); + void onFailsafeComboIndexChanged(int index); + void onFailsafeUsecChanged(int value); + void onFailsafePercentChanged(double value); + void onFailsafesDisplayValueTypeChanged(int type); + void onRfFreqChanged(int freq); + void updateFailsafe(unsigned channel); + void on_optionValue_valueChanged(int value); + void onClearAccessRxClicked(); + void on_chkOption_stateChanged(int state); + void on_cboOption_currentIndexChanged(int value); + + private: + enum FailsafeValueDisplayTypes { FAILSAFE_DISPLAY_PERCENT = 1, FAILSAFE_DISPLAY_USEC = 2 }; + + struct ChannelFailsafeWidgetsGroup { + QLabel * label; + QComboBox * combo; + QSpinBox * sbUsec; + QDoubleSpinBox * sbPercent; + }; + + ModuleData & module; + int moduleIdx; + Ui::Module *ui; + QMap failsafeGroupsMap; + static quint8 failsafesValueDisplayType; // FailsafeValueDisplayTypes + void updateFailsafeUI(unsigned channel, quint8 updtSb); + FilteredItemModel *trainerModeItemModel; + static bool isTrainerModule(int index) { return index < 0; } + static bool isInternalModule(int index) { return index == 0; } + static bool isExternalModule(int index) { return index > 0; } +}; diff --git a/companion/src/modeledit/setup_timer.cpp b/companion/src/modeledit/setup_timer.cpp new file mode 100644 index 000000000..2138a85b1 --- /dev/null +++ b/companion/src/modeledit/setup_timer.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * 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 "setup_timer.h" +#include "ui_setup_timer.h" +#include "namevalidator.h" + +TimerPanel::TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, + QWidget * prevFocus, FilteredItemModelFactory * panelFilteredModels, CompoundItemModelFactory * panelItemModels): + ModelPanel(parent, model, generalSettings, firmware), + timer(timer), + ui(new Ui::Timer), + modelsUpdateCnt(0) +{ + ui->setupUi(this); + connectItemModelEvents(panelFilteredModels->getItemModel(FIM_TIMERSWITCH)); + + lock = true; + + // Name + int length = firmware->getCapability(TimersName); + if (length == 0) + ui->name->hide(); + else { + ui->name->setValidator(new NameValidator(firmware->getBoard(), this)); + ui->name->setField(timer.name, length, this); + connect(ui->name, SIGNAL(currentDataChanged()), this, SLOT(onNameChanged())); + } + + ui->value->setField(timer.val, this); + ui->value->setMaximumTime(firmware->getMaxTimerStart()); + connect(ui->value, &AutoTimeEdit::currentDataChanged, [=](const int val) { + update(); + }); + + ui->mode->setModel(panelItemModels->getItemModel(AIM_TIMER_MODE)); + ui->mode->setField(timer.mode, this); + connect(ui->mode, SIGNAL(currentDataChanged(int)), this, SLOT(onModeChanged(int))); + + ui->swtch->setModel(panelFilteredModels->getItemModel(FIM_TIMERSWITCH)); + ui->swtch->setField(timer.swtch, this); + + ui->countdownBeep->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNBEEP)); + ui->countdownBeep->setField(timer.countdownBeep, this); + connect(ui->countdownBeep, SIGNAL(currentDataChanged(int)), this, SLOT(onCountdownBeepChanged(int))); + + ui->minuteBeep->setField(timer.minuteBeep, this); + + if (firmware->getCapability(PermTimers)) { + ui->persistent->setModel(panelItemModels->getItemModel(AIM_TIMER_PERSISTENT)); + ui->persistent->setField(timer.persistent, this); + } + else { + ui->persistent->hide(); + ui->persistentValue->hide(); + } + + ui->countdownStart->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNSTART)); + ui->countdownStart->setField(timer.countdownStart, this); + + + ui->showElapsed->setModel(panelItemModels->getItemModel(AIM_TIMER_SHOWELAPSED)); + ui->showElapsed->setField(timer.showElapsed, this); + + disableMouseScrolling(); + QWidget::setTabOrder(prevFocus, ui->name); + QWidget::setTabOrder(ui->name, ui->value); + QWidget::setTabOrder(ui->value, ui->mode); + QWidget::setTabOrder(ui->mode, ui->countdownBeep); + QWidget::setTabOrder(ui->countdownBeep, ui->countdownStart); + QWidget::setTabOrder(ui->countdownStart, ui->minuteBeep); + QWidget::setTabOrder(ui->minuteBeep, ui->persistent); + QWidget::setTabOrder(ui->persistent, ui->showElapsed); + + update(); + lock = false; +} + +TimerPanel::~TimerPanel() +{ + delete ui; +} + +void TimerPanel::update() +{ + lock = true; + + ui->name->updateValue(); + ui->mode->updateValue(); + ui->swtch->updateValue(); + ui->value->updateValue(); + ui->countdownBeep->updateValue(); + ui->minuteBeep->updateValue(); + ui->countdownStart->updateValue(); + ui->showElapsed->updateValue(); + + if (timer.mode != TimerData::TIMERMODE_OFF) + ui->swtch->setEnabled(true); + else + ui->swtch->setEnabled(false); + + if (timer.countdownBeep == TimerData::COUNTDOWNBEEP_SILENT) { + ui->countdownStartLabel->setEnabled(false); + ui->countdownStart->setEnabled(false); + } + else { + ui->countdownStartLabel->setEnabled(true); + ui->countdownStart->setEnabled(true); + } + + if (firmware->getCapability(PermTimers)) { + ui->persistent->updateValue(); + ui->persistentValue->setText(timer.pvalueToString()); + } + + if (timer.val) { + ui->showElapsed->setEnabled(true); + } + else { + ui->showElapsed->setEnabled(false); + } + + lock = false; +} + +QWidget * TimerPanel::getLastFocus() +{ + return ui->persistent; +} + +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; + modelsUpdateCnt++; +} + +void TimerPanel::onItemModelUpdateComplete() +{ + modelsUpdateCnt--; + if (modelsUpdateCnt < 1) { + update(); + lock = false; + } +} + +void TimerPanel::onNameChanged() +{ + emit nameChanged(); +} + +void TimerPanel::onCountdownBeepChanged(int index) +{ + timer.countdownBeepChanged(); + update(); +} + +void TimerPanel::onModeChanged(int index) +{ + timer.modeChanged(); + update(); + emit modeChanged(); +} diff --git a/companion/src/modeledit/setup_timer.h b/companion/src/modeledit/setup_timer.h new file mode 100644 index 000000000..34baf6001 --- /dev/null +++ b/companion/src/modeledit/setup_timer.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "modeledit.h" +#include "eeprominterface.h" +#include "filtereditemmodels.h" + +constexpr char FIM_TIMERSWITCH[] {"Timer Switch"}; +constexpr char MIMETYPE_TIMER[] {"application/x-companion-timer"}; + +namespace Ui { + class Timer; +} + +class TimerPanel : public ModelPanel +{ + Q_OBJECT + + public: + TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, + QWidget * prevFocus, FilteredItemModelFactory * panelFilteredModels, CompoundItemModelFactory * panelItemModels); + virtual ~TimerPanel(); + + virtual void update(); + QWidget * getLastFocus(); + + public slots: + void onNameChanged(); + void onModeChanged(int index); + + private slots: + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); + void onCountdownBeepChanged(int index); + + signals: + void nameChanged(); + void modeChanged(); + + private: + TimerData & timer; + Ui::Timer * ui; + void connectItemModelEvents(const FilteredItemModel * itemModel); + int modelsUpdateCnt; +};