diff --git a/companion/src/firmwares/opentx/opentxGruvin9xsimulator.cpp b/companion/src/firmwares/opentx/opentxGruvin9xsimulator.cpp index 3f83faeb9..232713199 100644 --- a/companion/src/firmwares/opentx/opentxGruvin9xsimulator.cpp +++ b/companion/src/firmwares/opentx/opentxGruvin9xsimulator.cpp @@ -75,6 +75,11 @@ namespace Open9xGruvin9x { #include "radio/src/vario.cpp" #include "radio/src/gui/menus.cpp" #include "radio/src/gui/menu_model.cpp" +#include "radio/src/gui/menu_model_select.cpp" +#include "radio/src/gui/menu_model_setup.cpp" +#include "radio/src/gui/menu_model_heli.cpp" +#include "radio/src/gui/menu_model_flightmodes.cpp" +#include "radio/src/gui/menu_model_inputs_mixes.cpp" #include "radio/src/gui/menu_model_logical_switches.cpp" #include "radio/src/gui/menu_model_custom_functions.cpp" #include "radio/src/gui/menu_model_curves.cpp" diff --git a/companion/src/firmwares/opentx/opentxM128simulator.cpp b/companion/src/firmwares/opentx/opentxM128simulator.cpp index 86ed9add1..d5900e869 100644 --- a/companion/src/firmwares/opentx/opentxM128simulator.cpp +++ b/companion/src/firmwares/opentx/opentxM128simulator.cpp @@ -76,6 +76,11 @@ namespace OpenTxM128 { #include "radio/src/vario.cpp" #include "radio/src/gui/menus.cpp" #include "radio/src/gui/menu_model.cpp" +#include "radio/src/gui/menu_model_select.cpp" +#include "radio/src/gui/menu_model_setup.cpp" +#include "radio/src/gui/menu_model_heli.cpp" +#include "radio/src/gui/menu_model_flightmodes.cpp" +#include "radio/src/gui/menu_model_inputs_mixes.cpp" #include "radio/src/gui/menu_model_curves.cpp" #include "radio/src/gui/menu_model_logical_switches.cpp" #include "radio/src/gui/menu_model_custom_functions.cpp" diff --git a/companion/src/firmwares/opentx/opentxM64simulator.cpp b/companion/src/firmwares/opentx/opentxM64simulator.cpp index 6c8d9a582..073fe1533 100644 --- a/companion/src/firmwares/opentx/opentxM64simulator.cpp +++ b/companion/src/firmwares/opentx/opentxM64simulator.cpp @@ -81,6 +81,11 @@ namespace OpenTxM64 { #include "radio/src/vario.cpp" #include "radio/src/gui/menus.cpp" #include "radio/src/gui/menu_model.cpp" +#include "radio/src/gui/menu_model_select.cpp" +#include "radio/src/gui/menu_model_setup.cpp" +#include "radio/src/gui/menu_model_heli.cpp" +#include "radio/src/gui/menu_model_flightmodes.cpp" +#include "radio/src/gui/menu_model_inputs_mixes.cpp" #include "radio/src/gui/menu_model_curves.cpp" #include "radio/src/gui/menu_model_logical_switches.cpp" #include "radio/src/gui/menu_model_custom_functions.cpp" diff --git a/companion/src/firmwares/opentx/opentxSky9xsimulator.cpp b/companion/src/firmwares/opentx/opentxSky9xsimulator.cpp index 65660db58..551e0a8c2 100644 --- a/companion/src/firmwares/opentx/opentxSky9xsimulator.cpp +++ b/companion/src/firmwares/opentx/opentxSky9xsimulator.cpp @@ -90,6 +90,11 @@ namespace Open9xSky9x { #include "radio/src/vario.cpp" #include "radio/src/gui/menus.cpp" #include "radio/src/gui/menu_model.cpp" +#include "radio/src/gui/menu_model_select.cpp" +#include "radio/src/gui/menu_model_setup.cpp" +#include "radio/src/gui/menu_model_heli.cpp" +#include "radio/src/gui/menu_model_flightmodes.cpp" +#include "radio/src/gui/menu_model_inputs_mixes.cpp" #include "radio/src/gui/menu_model_curves.cpp" #include "radio/src/gui/menu_model_logical_switches.cpp" #include "radio/src/gui/menu_model_custom_functions.cpp" diff --git a/companion/src/firmwares/opentx/opentxTaranisSimulator.cpp b/companion/src/firmwares/opentx/opentxTaranisSimulator.cpp index 35be1eae4..6e43b4386 100644 --- a/companion/src/firmwares/opentx/opentxTaranisSimulator.cpp +++ b/companion/src/firmwares/opentx/opentxTaranisSimulator.cpp @@ -96,6 +96,11 @@ inline int geteepromsize() { #include "radio/src/vario.cpp" #include "radio/src/gui/menus.cpp" #include "radio/src/gui/menu_model.cpp" +#include "radio/src/gui/menu_model_select.cpp" +#include "radio/src/gui/menu_model_setup.cpp" +#include "radio/src/gui/menu_model_heli.cpp" +#include "radio/src/gui/menu_model_flightmodes.cpp" +#include "radio/src/gui/menu_model_inputs_mixes.cpp" #include "radio/src/gui/menu_model_curves.cpp" #include "radio/src/gui/menu_model_logical_switches.cpp" #include "radio/src/gui/menu_model_custom_functions.cpp" diff --git a/companion/src/firmwares/opentx/opentxTaranisX9ESimulator.cpp b/companion/src/firmwares/opentx/opentxTaranisX9ESimulator.cpp index 4fb00cf08..0bd40e21c 100755 --- a/companion/src/firmwares/opentx/opentxTaranisX9ESimulator.cpp +++ b/companion/src/firmwares/opentx/opentxTaranisX9ESimulator.cpp @@ -98,12 +98,17 @@ inline int geteepromsize() { #include "radio/src/vario.cpp" #include "radio/src/gui/menus.cpp" #include "radio/src/gui/menu_model.cpp" +#include "radio/src/gui/menu_model_select.cpp" +#include "radio/src/gui/menu_model_setup.cpp" +#include "radio/src/gui/menu_model_heli.cpp" +#include "radio/src/gui/menu_model_flightmodes.cpp" +#include "radio/src/gui/menu_model_inputs_mixes.cpp" +#include "radio/src/gui/menu_model_limits.cpp" #include "radio/src/gui/menu_model_curves.cpp" #include "radio/src/gui/menu_model_logical_switches.cpp" #include "radio/src/gui/menu_model_custom_functions.cpp" #include "radio/src/gui/menu_model_custom_scripts.cpp" #include "radio/src/gui/menu_model_gvars.cpp" -#include "radio/src/gui/menu_model_limits.cpp" #include "radio/src/gui/menu_model_telemetry.cpp" #include "radio/src/gui/menu_general.cpp" #include "radio/src/gui/Taranis/view_main.cpp" diff --git a/radio/src/Makefile b/radio/src/Makefile index 88f0df201..7f74f0841 100644 --- a/radio/src/Makefile +++ b/radio/src/Makefile @@ -365,7 +365,7 @@ M128_VARIANT = +32768 M2561_VARIANT = +16384 EEPROM_VARIANT = 0 -GUIMODELSRC = gui/menu_model.cpp gui/menu_model_limits.cpp gui/menu_model_logical_switches.cpp gui/menu_model_custom_functions.cpp gui/menu_model_telemetry.cpp +GUIMODELSRC = gui/menu_model.cpp gui/menu_model_select.cpp gui/menu_model_setup.cpp gui/menu_model_inputs_mixes.cpp gui/menu_model_limits.cpp gui/menu_model_logical_switches.cpp gui/menu_model_custom_functions.cpp gui/menu_model_telemetry.cpp ifeq ($(PCB), $(filter $(PCB), STD 9X 9XR)) # 9x/9xr radio @@ -1012,6 +1012,7 @@ endif ifeq ($(FLIGHT_MODES), YES) CPPDEFS += -DFLIGHT_MODES + GUIMODELSRC += gui/menu_model_flightmodes.cpp endif ifeq ($(CURVES), YES) @@ -1166,6 +1167,7 @@ endif ifeq ($(HELI), YES) CPPDEFS += -DHELI + GUIMODELSRC += gui/menu_model_heli.cpp endif ifeq ($(TEMPLATES), YES) diff --git a/radio/src/gui/menu_model.cpp b/radio/src/gui/menu_model.cpp index 46f7ace1f..bf887c71d 100644 --- a/radio/src/gui/menu_model.cpp +++ b/radio/src/gui/menu_model.cpp @@ -83,8 +83,44 @@ void menuModelExpoOne(uint8_t event); extern uint8_t s_curveChan; +#if defined(CPUARM) + #define FlightModesType uint16_t +#else + #define FlightModesType uint8_t +#endif + #if defined(PCBTARANIS) -void editCurveRef(coord_t x, coord_t y, CurveRef & curve, uint8_t event, uint8_t attr); + void editCurveRef(coord_t x, coord_t y, CurveRef & curve, uint8_t event, uint8_t attr); +#endif + +#if MENU_COLUMNS < 2 + #if LCD_W >= 212 + #define MIXES_2ND_COLUMN (18*FW) + #else + #define MIXES_2ND_COLUMN (12*FW) + #endif +#else + #define MIXES_2ND_COLUMN (9*FW) +#endif + +#if MENU_COLUMNS > 1 +uint8_t editDelay(const coord_t x, const coord_t y, const uint8_t event, const uint8_t attr, const pm_char *str, uint8_t delay) +{ + lcd_puts(x, y, str); + lcd_outdezAtt(x+MIXES_2ND_COLUMN, y, (10/DELAY_STEP)*delay, attr|PREC1|LEFT); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, delay, DELAY_MAX); + return delay; +} +#define EDIT_DELAY(x, y, event, attr, str, delay) editDelay(x, y, event, attr, str, delay) +#else +uint8_t editDelay(const coord_t y, const uint8_t event, const uint8_t attr, const pm_char *str, uint8_t delay) +{ + lcd_putsLeft(y, str); + lcd_outdezAtt(MIXES_2ND_COLUMN, y, (10/DELAY_STEP)*delay, attr|PREC1|LEFT); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, delay, DELAY_MAX); + return delay; +} +#define EDIT_DELAY(x, y, event, attr, str, delay) editDelay(y, event, attr, str, delay) #endif const MenuFuncP_PROGMEM menuTabModel[] PROGMEM = { @@ -115,671 +151,6 @@ static uint8_t s_copyMode = 0; static int8_t s_copySrcRow; static int8_t s_copyTgtOfs; -uint8_t eeFindEmptyModel(uint8_t id, bool down) -{ - uint8_t i = id; - for (;;) { - i = (MAX_MODELS + (down ? i+1 : i-1)) % MAX_MODELS; - if (!eeModelExists(i)) break; - if (i == id) return 0xff; // no free space in directory left - } - return i; -} - -void selectModel(uint8_t sub) -{ - displayPopup(STR_LOADINGMODEL); - saveTimers(); - eeCheck(true); // force writing of current model data before this is changed - g_eeGeneral.currModel = sub; - eeDirty(EE_GENERAL); - eeLoadModel(sub); -} - -#if defined(SDCARD) -#define LIST_NONE_SD_FILE 1 -bool listSdFiles(const char *path, const char *extension, const uint8_t maxlen, const char *selection, uint8_t flags=0) -{ - FILINFO fno; - DIR dir; - char *fn; /* This function is assuming non-Unicode cfg. */ -#if _USE_LFN - TCHAR lfn[_MAX_LFN + 1]; - fno.lfname = lfn; - fno.lfsize = sizeof(lfn); -#endif - - static uint16_t s_last_menu_offset = 0; - -#if defined(CPUARM) - static uint8_t s_last_flags; - - if (selection) { - s_last_flags = flags; - memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); - strcpy(reusableBuffer.modelsel.menu_bss[0], path); - strcat(reusableBuffer.modelsel.menu_bss[0], "/"); - strncat(reusableBuffer.modelsel.menu_bss[0], selection, maxlen); - strcat(reusableBuffer.modelsel.menu_bss[0], extension); - if (f_stat(reusableBuffer.modelsel.menu_bss[0], &fno) != FR_OK) { - selection = NULL; - } - } - else { - flags = s_last_flags; - } -#endif - - if (s_menu_offset == 0) { - s_last_menu_offset = 0; - memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); - } - else if (s_menu_offset == s_menu_count - MENU_MAX_LINES) { - s_last_menu_offset = 0xffff; - memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); - } - else if (s_menu_offset == s_last_menu_offset) { - // should not happen, only there because of Murphy's law - return true; - } - else if (s_menu_offset > s_last_menu_offset) { - memmove(reusableBuffer.modelsel.menu_bss[0], reusableBuffer.modelsel.menu_bss[1], (MENU_MAX_LINES-1)*MENU_LINE_LENGTH); - memset(reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-1], 0xff, MENU_LINE_LENGTH); - } - else { - memmove(reusableBuffer.modelsel.menu_bss[1], reusableBuffer.modelsel.menu_bss[0], (MENU_MAX_LINES-1)*MENU_LINE_LENGTH); - memset(reusableBuffer.modelsel.menu_bss[0], 0, MENU_LINE_LENGTH); - } - - s_menu_count = 0; - s_menu_flags = BSS; - - FRESULT res = f_opendir(&dir, path); /* Open the directory */ - if (res == FR_OK) { - - if (flags & LIST_NONE_SD_FILE) { - s_menu_count++; - if (selection) { - s_last_menu_offset++; - } - else if (s_menu_offset==0 || s_menu_offset < s_last_menu_offset) { - char *line = reusableBuffer.modelsel.menu_bss[0]; - memset(line, 0, MENU_LINE_LENGTH); - strcpy(line, "---"); - s_menu[0] = line; - } - } - - for (;;) { - res = f_readdir(&dir, &fno); /* Read a directory item */ - if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ - -#if _USE_LFN - fn = *fno.lfname ? fno.lfname : fno.fname; -#else - fn = fno.fname; -#endif - - uint8_t len = strlen(fn); - if (len < 5 || len > maxlen+4 || strcasecmp(fn+len-4, extension) || (fno.fattrib & AM_DIR)) continue; - - s_menu_count++; - fn[len-4] = '\0'; - - if (s_menu_offset == 0) { - if (selection && strncasecmp(fn, selection, maxlen) < 0) { - s_last_menu_offset++; - } - else { - for (uint8_t i=0; i=0; i--) { - char *line = reusableBuffer.modelsel.menu_bss[i]; - if (line[0] == '\0' || strcasecmp(fn, line) > 0) { - if (i > 0) memmove(reusableBuffer.modelsel.menu_bss[0], reusableBuffer.modelsel.menu_bss[1], sizeof(reusableBuffer.modelsel.menu_bss[i]) * i); - memset(line, 0, MENU_LINE_LENGTH); - strcpy(line, fn); - break; - } - } - for (uint8_t i=0; i s_last_menu_offset) { - if (strcasecmp(fn, reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-2]) > 0 && strcasecmp(fn, reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-1]) < 0) { - memset(reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-1], 0, MENU_LINE_LENGTH); - strcpy(reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-1], fn); - } - } - else { - if (strcasecmp(fn, reusableBuffer.modelsel.menu_bss[1]) < 0 && strcasecmp(fn, reusableBuffer.modelsel.menu_bss[0]) > 0) { - memset(reusableBuffer.modelsel.menu_bss[0], 0, MENU_LINE_LENGTH); - strcpy(reusableBuffer.modelsel.menu_bss[0], fn); - } - } - } - } - - if (s_menu_offset > 0) - s_last_menu_offset = s_menu_offset; - else - s_menu_offset = s_last_menu_offset; - - return s_menu_count; -} -#endif - -#if defined(PCBTARANIS) - static int8_t modelselBitmapIdx; - static uint8_t modelselBitmap[MODEL_BITMAP_SIZE]; - #define MODELSEL_W 133 - #define BMP_DIRTY() modelselBitmapIdx = -1 -#else - #define MODELSEL_W LCD_W - #define BMP_DIRTY() -#endif - -#if defined(NAVIGATION_MENUS) -void onModelSelectMenu(const char *result) -{ - int8_t sub = m_posVert; - - if (result == STR_SELECT_MODEL || result == STR_CREATE_MODEL) { - selectModel(sub); - } - else if (result == STR_COPY_MODEL) { - s_copyMode = COPY_MODE; - s_copyTgtOfs = 0; - s_copySrcRow = -1; - } - else if (result == STR_MOVE_MODEL) { - s_copyMode = MOVE_MODE; - s_copyTgtOfs = 0; - s_copySrcRow = -1; - } -#if defined(SDCARD) - else if (result == STR_BACKUP_MODEL) { - eeCheck(true); // force writing of current model data before this is changed - POPUP_WARNING(eeBackupModel(sub)); - } - else if (result == STR_RESTORE_MODEL || result == STR_UPDATE_LIST) { - if (!listSdFiles(MODELS_PATH, MODELS_EXT, MENU_LINE_LENGTH-1, NULL)) { - POPUP_WARNING(STR_NO_MODELS_ON_SD); - s_menu_flags = 0; - } - } -#endif - else if (result == STR_DELETE_MODEL) { - POPUP_CONFIRMATION(STR_DELETEMODEL); -#if defined(CPUARM) - SET_WARNING_INFO(modelHeaders[sub].name, sizeof(g_model.header.name), ZCHAR); -#else - char * name = reusableBuffer.modelsel.mainname; - eeLoadModelName(sub, name); - SET_WARNING_INFO(name, sizeof(g_model.header.name), ZCHAR); -#endif - } -#if defined(SDCARD) - else { - // The user choosed a file on SD to restore - POPUP_WARNING(eeRestoreModel(sub, (char *)result)); - BMP_DIRTY(); - if (!s_warning && g_eeGeneral.currModel == sub) - eeLoadModel(sub); - } -#endif -} -#endif - -void menuModelSelect(uint8_t event) -{ - if (s_warning_result) { - s_warning_result = 0; - eeDeleteModel(m_posVert); // delete file - s_copyMode = 0; - event = EVT_ENTRY_UP; - BMP_DIRTY(); - } - - uint8_t _event_ = (IS_ROTARY_BREAK(event) || IS_ROTARY_LONG(event) ? 0 : event); - -#if defined(PCBTARANIS) - if ((s_copyMode && EVT_KEY_MASK(event) == KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) - _event_ -= KEY_EXIT; -#else - if (s_copyMode && EVT_KEY_MASK(event) == KEY_EXIT) - _event_ -= KEY_EXIT; -#endif - - int8_t oldSub = m_posVert; - - check_submenu_simple(_event_, MAX_MODELS-1); - -#if defined(NAVIGATION_POT2) - if (event==0 && p2valdiff<0) { - event = EVT_KEY_FIRST(KEY_RIGHT); - } -#endif - - if (s_editMode > 0) s_editMode = 0; - -#if !defined(CPUARM) - if (event) { - eeFlush(); // flush eeprom write - } -#endif - - int8_t sub = m_posVert; - - switch (event) - { - case EVT_ENTRY: - m_posVert = sub = g_eeGeneral.currModel; - if (sub >= LCD_LINES-1) s_pgOfs = sub-LCD_LINES+2; - s_copyMode = 0; - s_editMode = EDIT_MODE_INIT; - BMP_DIRTY(); - eeCheck(true); - break; - case EVT_KEY_LONG(KEY_EXIT): - if (s_copyMode && s_copyTgtOfs == 0 && g_eeGeneral.currModel != sub && eeModelExists(sub)) { - POPUP_CONFIRMATION(STR_DELETEMODEL); -#if defined(CPUARM) - SET_WARNING_INFO(modelHeaders[sub].name, sizeof(g_model.header.name), ZCHAR); -#else - char * name = reusableBuffer.modelsel.mainname; - eeLoadModelName(sub, name); - SET_WARNING_INFO(name, sizeof(g_model.header.name), ZCHAR); -#endif - killEvents(event); - break; - } - // no break -#if defined(ROTARY_ENCODER_NAVIGATION) - case EVT_ROTARY_LONG: - killEvents(event); - if (s_editMode < 0) { - popMenu(); - break; - } - else if (!s_copyMode) { - m_posVert = sub = g_eeGeneral.currModel; - s_copyMode = 0; - s_editMode = EDIT_MODE_INIT; - } - // no break -#endif - case EVT_KEY_BREAK(KEY_EXIT): - if (s_copyMode) { - sub = m_posVert = (s_copyMode == MOVE_MODE || s_copySrcRow<0) ? (MAX_MODELS+sub+s_copyTgtOfs) % MAX_MODELS : s_copySrcRow; - s_copyMode = 0; - } -#if defined(PCBTARANIS) - else { - if (m_posVert != g_eeGeneral.currModel) { - m_posVert = g_eeGeneral.currModel; - s_pgOfs = 0; - } - else if (event != EVT_KEY_LONG(KEY_EXIT)) { - popMenu(); - } - } -#endif - break; -#if defined(ROTARY_ENCODER_NAVIGATION) - case EVT_ROTARY_BREAK: - if (s_editMode == -1) { - s_editMode = 0; - break; - } - // no break; -#endif - case EVT_KEY_LONG(KEY_ENTER): - case EVT_KEY_BREAK(KEY_ENTER): - s_editMode = 0; - if (READ_ONLY()) { - if (g_eeGeneral.currModel != sub && eeModelExists(sub)) { - selectModel(sub); - } - } - else if (s_copyMode && (s_copyTgtOfs || s_copySrcRow>=0)) { - displayPopup(s_copyMode==COPY_MODE ? STR_COPYINGMODEL : STR_MOVINGMODEL); - eeCheck(true); // force writing of current model data before this is changed - - uint8_t cur = (MAX_MODELS + sub + s_copyTgtOfs) % MAX_MODELS; - - if (s_copyMode == COPY_MODE) { - if (eeCopyModel(cur, s_copySrcRow)) - BMP_DIRTY(); - else - cur = sub; - } - - s_copySrcRow = g_eeGeneral.currModel; // to update the currModel value - while (sub != cur) { - uint8_t src = cur; - cur = (s_copyTgtOfs > 0 ? cur+MAX_MODELS-1 : cur+1) % MAX_MODELS; - eeSwapModels(src, cur); - BMP_DIRTY(); - if (src == s_copySrcRow) - s_copySrcRow = cur; - else if (cur == s_copySrcRow) - s_copySrcRow = src; - } - - if (s_copySrcRow != g_eeGeneral.currModel) { - g_eeGeneral.currModel = s_copySrcRow; - eeDirty(EE_GENERAL); - } - - s_copyMode = 0; - event = EVT_ENTRY_UP; - } - else if (event == EVT_KEY_LONG(KEY_ENTER) -#if !defined(PCBTARANIS) - || IS_ROTARY_BREAK(event) -#endif - ) { - s_copyMode = 0; - killEvents(event); -#if defined(NAVIGATION_MENUS) - if (g_eeGeneral.currModel != sub) { - if (eeModelExists(sub)) { - MENU_ADD_ITEM(STR_SELECT_MODEL); - MENU_ADD_SD_ITEM(STR_BACKUP_MODEL); - MENU_ADD_ITEM(STR_COPY_MODEL); - MENU_ADD_ITEM(STR_MOVE_MODEL); - MENU_ADD_ITEM(STR_DELETE_MODEL); - } - else { -#if defined(SDCARD) - MENU_ADD_ITEM(STR_CREATE_MODEL); - MENU_ADD_ITEM(STR_RESTORE_MODEL); -#else - selectModel(sub); -#endif - } - } - else { - MENU_ADD_SD_ITEM(STR_BACKUP_MODEL); - MENU_ADD_ITEM(STR_COPY_MODEL); - MENU_ADD_ITEM(STR_MOVE_MODEL); - } - menuHandler = onModelSelectMenu; -#else - if (g_eeGeneral.currModel != sub) { - selectModel(sub); - } -#endif - } - else if (eeModelExists(sub)) { - s_copyMode = (s_copyMode == COPY_MODE ? MOVE_MODE : COPY_MODE); - s_copyTgtOfs = 0; - s_copySrcRow = -1; - } - break; - -#if defined(PCBTARANIS) - case EVT_KEY_BREAK(KEY_PAGE): - case EVT_KEY_LONG(KEY_PAGE): - chainMenu(event == EVT_KEY_BREAK(KEY_PAGE) ? menuModelSetup : menuTabModel[DIM(menuTabModel)-1]); - killEvents(event); - break; -#else -#if defined(ROTARY_ENCODER_NAVIGATION) - case EVT_ROTARY_LEFT: - case EVT_ROTARY_RIGHT: -#endif - case EVT_KEY_FIRST(KEY_LEFT): - case EVT_KEY_FIRST(KEY_RIGHT): -#if defined(ROTARY_ENCODER_NAVIGATION) - if ((!IS_ROTARY_RIGHT(event) && !IS_ROTARY_LEFT(event)) || s_editMode < 0) { -#endif - if (sub == g_eeGeneral.currModel) { - chainMenu((IS_ROTARY_RIGHT(event) || event == EVT_KEY_FIRST(KEY_RIGHT)) ? menuModelSetup : menuTabModel[DIM(menuTabModel)-1]); - } - else { - AUDIO_WARNING2(); - } - break; -#if defined(ROTARY_ENCODER_NAVIGATION) - } - // no break -#endif -#endif - - case EVT_KEY_FIRST(KEY_MOVE_UP): - case EVT_KEY_REPT(KEY_MOVE_UP): - case EVT_KEY_FIRST(KEY_MOVE_DOWN): - case EVT_KEY_REPT(KEY_MOVE_DOWN): - if (s_copyMode) { - int8_t next_ofs = s_copyTgtOfs + oldSub - m_posVert; - if (next_ofs == MAX_MODELS || next_ofs == -MAX_MODELS) - next_ofs = 0; - - if (s_copySrcRow < 0 && s_copyMode==COPY_MODE) { - s_copySrcRow = oldSub; - // find a hole (in the first empty slot above / below) - sub = eeFindEmptyModel(s_copySrcRow, IS_ROTARY_DOWN(event) || event==EVT_KEY_FIRST(KEY_MOVE_DOWN)); - if (sub < 0) { - // no free room for duplicating the model - AUDIO_ERROR(); - sub = oldSub; - s_copyMode = 0; - } - next_ofs = 0; - m_posVert = sub; - } - s_copyTgtOfs = next_ofs; - } - break; - } - -#if defined(PCBHORUS) -#elif defined(PCBTARANIS) - lcd_puts(27*FW-(LEN_FREE-4)*FW, 0, STR_FREE); - if (event) reusableBuffer.modelsel.eepromfree = EeFsGetFree(); - lcd_outdezAtt(20*FW, 0, reusableBuffer.modelsel.eepromfree, 0); - lcd_puts(21*FW, 0, STR_BYTES); -#elif !defined(PCBSKY9X) - lcd_puts(9*FW-(LEN_FREE-4)*FW, 0, STR_FREE); - if (event) reusableBuffer.modelsel.eepromfree = EeFsGetFree(); - lcd_outdezAtt(17*FW, 0, reusableBuffer.modelsel.eepromfree, 0); -#endif - -#if defined(PCBHORUS) - // nothing -#elif defined(PCBTARANIS) - displayScreenIndex(e_ModelSelect, DIM(menuTabModel), 0); - lcd_filled_rect(0, 0, LCD_W, FH, SOLID, FILL_WHITE|GREY_DEFAULT); -#elif defined(ROTARY_ENCODER_NAVIGATION) - displayScreenIndex(e_ModelSelect, DIM(menuTabModel), (sub == g_eeGeneral.currModel) ? ((IS_RE_NAVIGATION_ENABLE() && s_editMode < 0) ? INVERS|BLINK : INVERS) : 0); -#else - displayScreenIndex(e_ModelSelect, DIM(menuTabModel), (sub == g_eeGeneral.currModel) ? INVERS : 0); -#endif - - TITLE(STR_MENUMODELSEL); - - for (uint8_t i=0; i= 0)) { - if (k == sub) { - if (s_copyMode == COPY_MODE) { - k = s_copySrcRow; - lcd_putc(MODELSEL_W-FW, y, '+'); - } - else { - k = sub + s_copyTgtOfs; - } - } - else if (s_copyTgtOfs < 0 && ((k < sub && k >= sub+s_copyTgtOfs) || (k-MAX_MODELS < sub && k-MAX_MODELS >= sub+s_copyTgtOfs))) - k += 1; - else if (s_copyTgtOfs > 0 && ((k > sub && k <= sub+s_copyTgtOfs) || (k+MAX_MODELS > sub && k+MAX_MODELS <= sub+s_copyTgtOfs))) - k += MAX_MODELS-1; - } - - k %= MAX_MODELS; - - if (eeModelExists(k)) { -#if defined(PCBSKY9X) - putsModelName(4*FW, y, modelHeaders[k].name, k, 0); -#elif defined(CPUARM) - putsModelName(4*FW, y, modelHeaders[k].name, k, 0); - lcd_outdezAtt(20*FW, y, eeModelSize(k), 0); -#else - char * name = reusableBuffer.modelsel.listnames[i]; - if (event) eeLoadModelName(k, name); - putsModelName(4*FW, y, name, k, 0); - lcd_outdezAtt(20*FW, y, eeModelSize(k), 0); -#endif - if (k==g_eeGeneral.currModel && (s_copyMode!=COPY_MODE || s_copySrcRow<0 || i+s_pgOfs!=(vertpos_t)sub)) lcd_putc(1, y, '*'); - } - - if (s_copyMode && (vertpos_t)sub==i+s_pgOfs) { - lcd_filled_rect(9, y, MODELSEL_W-1-9, 7); - lcd_rect(8, y-1, MODELSEL_W-1-7, 9, s_copyMode == COPY_MODE ? SOLID : DOTTED); - } - } - -#if defined(PCBTARANIS) - if (modelselBitmapIdx != m_posVert) { - modelselBitmapIdx = m_posVert; - if (modelselBitmapIdx == g_eeGeneral.currModel) - memcpy(modelselBitmap, modelBitmap, MODEL_BITMAP_SIZE); - else - loadModelBitmap(modelHeaders[sub].bitmap, modelselBitmap); - } -#if !defined(PCBHORUS) - lcd_bmp(22*FW+2, 2*FH+FH/2, modelselBitmap); -#endif -#endif -} - -#if defined(CPUARM) -uint8_t g_moduleIdx; -void menuModelFailsafe(uint8_t event) -{ - static bool longNames = false; - bool newLongNames = false; - uint8_t ch = 0; - - if (event == EVT_KEY_LONG(KEY_ENTER) && s_editMode) { - s_noHi = NO_HI_LEN; - g_model.moduleData[g_moduleIdx].failsafeChannels[m_posVert] = channelOutputs[m_posVert]; - eeDirty(EE_MODEL); - AUDIO_WARNING1(); - SEND_FAILSAFE_NOW(g_moduleIdx); - } - - SIMPLE_SUBMENU_NOTITLE(NUM_CHNOUT); - - SET_SCROLLBAR_X(0); - -#if LCD_W >= 212 - #define COL_W (LCD_W/2) - const uint8_t SLIDER_W = 64; - // Column separator - lcd_vline(LCD_W/2, FH, LCD_H-FH); - - if (m_posVert >= 16) { - ch = 16; - } -#else - #define COL_W (LCD_W) - const uint8_t SLIDER_W = 90; - ch = 8 * (m_posVert / 8); -#endif - - lcd_putsCenter(0*FH, FAILSAFESET); - lcd_invert_line(0); - -#if LCD_W >= 212 - for (uint8_t col=0; col<2; col++) -#else - uint8_t col = 0; -#endif - { - coord_t x = col*COL_W+1; - - // Channels - for (uint8_t line=0; line<8; line++) { - coord_t y = 9+line*7; - int32_t val; - uint8_t ofs = (col ? 0 : 1); - - if (ch < g_model.moduleData[g_moduleIdx].channelsStart || ch >= NUM_CHANNELS(g_moduleIdx) + g_model.moduleData[g_moduleIdx].channelsStart) - val = 0; - else if (s_editMode && m_posVert == ch) - val = channelOutputs[ch]; - else - val = g_model.moduleData[g_moduleIdx].failsafeChannels[8*col+line]; - -#if defined(PCBTARANIS) - // Channel name if present, number if not - uint8_t lenLabel = ZLEN(g_model.limitData[ch].name); - if (lenLabel > 4) { - newLongNames = longNames = true; - } - - if (lenLabel > 0) - lcd_putsnAtt(x+1-ofs, y, g_model.limitData[ch].name, sizeof(g_model.limitData[ch].name), ZCHAR | SMLSIZE); - else - putsChn(x+1-ofs, y, ch+1, SMLSIZE); -#else - putsChn(x+1-ofs, y, ch+1, SMLSIZE); -#endif - - // Value - LcdFlags flags = TINSIZE; - if (m_posVert == ch && !s_noHi) { - flags |= INVERS; - if (s_editMode) - flags |= BLINK; - } -#if defined(PPM_UNIT_US) - uint8_t wbar = (longNames ? SLIDER_W-10 : SLIDER_W); - lcd_outdezAtt(x+COL_W-4-wbar-ofs, y, PPM_CH_CENTER(ch)+val/2, flags); -#elif defined(PPM_UNIT_PERCENT_PREC1) - uint8_t wbar = (longNames ? SLIDER_W-16 : SLIDER_W-6); - lcd_outdezAtt(x+COL_W-4-wbar-ofs, y, calcRESXto1000(val), PREC1|flags); -#else - uint8_t wbar = (longNames ? SLIDER_W-10 : SLIDER_W); - lcd_outdezAtt(x+COL_W-4-wbar-ofs, y, calcRESXto1000(val)/10, flags); -#endif - - // Gauge - lcd_rect(x+COL_W-3-wbar-ofs, y, wbar+1, 6); - uint16_t lim = g_model.extendedLimits ? 640*2 : 512*2; - uint8_t len = limit((uint8_t)1, uint8_t((abs(val) * wbar/2 + lim/2) / lim), uint8_t(wbar/2)); - coord_t x0 = (val>0) ? x+COL_W-ofs-3-wbar/2 : x+COL_W-ofs-2-wbar/2-len; - lcd_hline(x0, y+1, len); - lcd_hline(x0, y+2, len); - lcd_hline(x0, y+3, len); - lcd_hline(x0, y+4, len); - - ch++; - } - } - - longNames = newLongNames; -} -#endif - #if defined(CPUM64) #define editNameCursorPos m_posHorz #else @@ -891,2703 +262,8 @@ void editSingleName(coord_t x, coord_t y, const pm_char *label, char *name, uint } #endif -enum menuModelSetupItems { - ITEM_MODEL_NAME, - CASE_PCBTARANIS(ITEM_MODEL_BITMAP) - ITEM_MODEL_TIMER1, - CASE_CPUARM(ITEM_MODEL_TIMER1_NAME) - CASE_PERSISTENT_TIMERS(ITEM_MODEL_TIMER1_PERSISTENT) - ITEM_MODEL_TIMER1_MINUTE_BEEP, - ITEM_MODEL_TIMER1_COUNTDOWN_BEEP, - ITEM_MODEL_TIMER2, - CASE_CPUARM(ITEM_MODEL_TIMER2_NAME) - CASE_PERSISTENT_TIMERS(ITEM_MODEL_TIMER2_PERSISTENT) - ITEM_MODEL_TIMER2_MINUTE_BEEP, - ITEM_MODEL_TIMER2_COUNTDOWN_BEEP, - CASE_CPUARM(ITEM_MODEL_TIMER3) - CASE_CPUARM(ITEM_MODEL_TIMER3_NAME) - CASE_CPUARM(ITEM_MODEL_TIMER3_PERSISTENT) - CASE_CPUARM(ITEM_MODEL_TIMER3_MINUTE_BEEP) - CASE_CPUARM(ITEM_MODEL_TIMER3_COUNTDOWN_BEEP) - ITEM_MODEL_EXTENDED_LIMITS, - ITEM_MODEL_EXTENDED_TRIMS, - CASE_CPUARM(ITEM_MODEL_DISPLAY_TRIMS) - ITEM_MODEL_TRIM_INC, - CASE_PCBTARANIS(ITEM_MODEL_THROTTLE_LABEL) - ITEM_MODEL_THROTTLE_REVERSED, - ITEM_MODEL_THROTTLE_TRACE, - ITEM_MODEL_THROTTLE_TRIM, - CASE_CPUARM(ITEM_MODEL_PREFLIGHT_LABEL) - CASE_CPUARM(ITEM_MODEL_CHECKLIST_DISPLAY) - ITEM_MODEL_THROTTLE_WARNING, - ITEM_MODEL_SWITCHES_WARNING, - CASE_PCBTARANIS(ITEM_MODEL_SWITCHES_WARNING2) - CASE_PCBTARANIS(ITEM_MODEL_POT_WARNING) - ITEM_MODEL_BEEP_CENTER, - CASE_CPUARM(ITEM_MODEL_USE_GLOBAL_FUNCTIONS) -#if defined(PCBTARANIS) - ITEM_MODEL_INTERNAL_MODULE_LABEL, - ITEM_MODEL_INTERNAL_MODULE_MODE, - ITEM_MODEL_INTERNAL_MODULE_CHANNELS, - ITEM_MODEL_INTERNAL_MODULE_BIND, - ITEM_MODEL_INTERNAL_MODULE_FAILSAFE, - ITEM_MODEL_EXTERNAL_MODULE_LABEL, - ITEM_MODEL_EXTERNAL_MODULE_MODE, - ITEM_MODEL_EXTERNAL_MODULE_CHANNELS, - ITEM_MODEL_EXTERNAL_MODULE_BIND, - ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE, - ITEM_MODEL_TRAINER_LABEL, - ITEM_MODEL_TRAINER_MODE, - ITEM_MODEL_TRAINER_CHANNELS, - ITEM_MODEL_TRAINER_SETTINGS, -#elif defined(CPUARM) - ITEM_MODEL_EXTERNAL_MODULE_LABEL, - ITEM_MODEL_EXTERNAL_MODULE_MODE, - ITEM_MODEL_EXTERNAL_MODULE_CHANNELS, - ITEM_MODEL_EXTERNAL_MODULE_BIND, - ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE, -#if defined(PCBSKY9X) && !defined(REVA) && !defined(REVX) - ITEM_MODEL_EXTRA_MODULE_LABEL, - ITEM_MODEL_EXTRA_MODULE_CHANNELS, - ITEM_MODEL_EXTRA_MODULE_BIND, -#endif -#else - ITEM_MODEL_PPM1_PROTOCOL, - ITEM_MODEL_PPM1_PARAMS, -#endif - ITEM_MODEL_SETUP_MAX -}; - -#if defined(PCBSKY9X) - #define FIELD_PROTOCOL_MAX 2 -#else - #define FIELD_PROTOCOL_MAX 1 -#endif - -#if LCD_W >= 212 - #define MODEL_SETUP_2ND_COLUMN (LCD_W-17*FW-MENUS_SCROLLBAR_WIDTH) - #define MODEL_SETUP_BIND_OFS 3*FW-2 - #define MODEL_SETUP_RANGE_OFS 7*FW - #define MODEL_SETUP_SET_FAILSAFE_OFS 10*FW -#else - #define MODEL_SETUP_2ND_COLUMN (LCD_W-11*FW-MENUS_SCROLLBAR_WIDTH) - #define MODEL_SETUP_BIND_OFS 2*FW+1 - #define MODEL_SETUP_RANGE_OFS 4*FW+3 - #define MODEL_SETUP_SET_FAILSAFE_OFS 7*FW-2 -#endif - -#if defined(PCBTARANIS) && defined(SDCARD) -void copySelection(char *dst, const char *src, uint8_t size) -{ - if (memcmp(src, "---", 3) == 0) - memset(dst, 0, size); - else - memcpy(dst, src, size); -} - -void onModelSetupBitmapMenu(const char *result) -{ - if (result == STR_UPDATE_LIST) { - if (!listSdFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap), NULL)) { - POPUP_WARNING(STR_NO_BITMAPS_ON_SD); - s_menu_flags = 0; - } - } - else { - // The user choosed a bmp file in the list - copySelection(g_model.header.bitmap, result, sizeof(g_model.header.bitmap)); - LOAD_MODEL_BITMAP(); - memcpy(modelHeaders[g_eeGeneral.currModel].bitmap, g_model.header.bitmap, sizeof(g_model.header.bitmap)); - eeDirty(EE_MODEL); - } -} -#endif - -#if defined(PCBTARANIS) - #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_TRAINER_LABEL ? TRAINER_MODULE : (k>=ITEM_MODEL_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE)) -#elif defined(PCBSKY9X) && !defined(REVA) && !defined(REVX) - #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_EXTRA_MODULE_LABEL ? EXTRA_MODULE : EXTERNAL_MODULE) -#else - #define CURRENT_MODULE_EDITED(k) (EXTERNAL_MODULE) -#endif - -void menuModelSetup(uint8_t event) -{ -#if defined(PCBTARANIS) - horzpos_t l_posHorz = m_posHorz; - #define IF_INTERNAL_MODULE_ON(x) (g_model.moduleData[INTERNAL_MODULE].rfProtocol == RF_PROTO_OFF ? HIDDEN_ROW : (uint8_t)(x)) - #define IF_EXTERNAL_MODULE_ON(x) (g_model.externalModule == MODULE_TYPE_NONE ? HIDDEN_ROW : (uint8_t)(x)) - #define IF_TRAINER_ON(x) (g_model.trainerMode == TRAINER_MODE_SLAVE ? (uint8_t)(x) : HIDDEN_ROW) - #define IF_EXTERNAL_MODULE_XJT(x) (IS_MODULE_XJT(EXTERNAL_MODULE) ? (uint8_t)x : HIDDEN_ROW) - #define IS_D8_RX(x) (g_model.moduleData[x].rfProtocol == RF_PROTO_D8) - #define INTERNAL_MODULE_CHANNELS_ROWS() IF_INTERNAL_MODULE_ON(1) - #define EXTERNAL_MODULE_CHANNELS_ROWS() IF_EXTERNAL_MODULE_ON(IS_MODULE_DSM2(EXTERNAL_MODULE) ? (uint8_t)0 : (uint8_t)1) - #define TRAINER_CHANNELS_ROWS() IF_TRAINER_ON(1) - #define PORT_CHANNELS_ROWS(x) (x==INTERNAL_MODULE ? INTERNAL_MODULE_CHANNELS_ROWS() : (x==EXTERNAL_MODULE ? EXTERNAL_MODULE_CHANNELS_ROWS() : TRAINER_CHANNELS_ROWS())) - #define FAILSAFE_ROWS(x) ((g_model.moduleData[x].rfProtocol==RF_PROTO_X16 || g_model.moduleData[x].rfProtocol==RF_PROTO_LR12) ? (g_model.moduleData[x].failsafeMode==FAILSAFE_CUSTOM ? (uint8_t)1 : (uint8_t)0) : HIDDEN_ROW) - #define MODEL_SETUP_MAX_LINES (1+ITEM_MODEL_SETUP_MAX) - #define POT_WARN_ITEMS() ((g_model.nPotsToWarn >> 6) ? (uint8_t)NUM_POTS : (uint8_t)0) - #define TIMER_ROWS 2, 0, CASE_PERSISTENT_TIMERS(0) 0, 0 - bool CURSOR_ON_CELL = (m_posHorz >= 0); - MENU_TAB({ 0, 0, CASE_PCBTARANIS(0) TIMER_ROWS, TIMER_ROWS, TIMER_ROWS, 0, 1, 0, 0, CASE_PCBTARANIS(LABEL(Throttle)) 0, 0, 0, CASE_CPUARM(LABEL(PreflightCheck)) CASE_CPUARM(0) 0, uint8_t(NAVIGATION_LINE_BY_LINE|getSwitchWarningsAllowed()), ONE_2x2POS_DEFINED() ? TITLE_ROW : HIDDEN_ROW, POT_WARN_ITEMS(), NAVIGATION_LINE_BY_LINE|(NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS-1), 0, LABEL(InternalModule), 0, IF_INTERNAL_MODULE_ON(1), IF_INTERNAL_MODULE_ON(IS_D8_RX(0) ? (uint8_t)1 : (uint8_t)2), IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), LABEL(ExternalModule), (IS_MODULE_XJT(EXTERNAL_MODULE) || IS_MODULE_DSM2(EXTERNAL_MODULE)) ? (uint8_t)1 : (uint8_t)0, EXTERNAL_MODULE_CHANNELS_ROWS(), (IS_MODULE_XJT(EXTERNAL_MODULE) && IS_D8_RX(EXTERNAL_MODULE)) ? (uint8_t)1 : (IS_MODULE_PPM(EXTERNAL_MODULE) || IS_MODULE_XJT(EXTERNAL_MODULE) || IS_MODULE_DSM2(EXTERNAL_MODULE)) ? (uint8_t)2 : HIDDEN_ROW, IF_EXTERNAL_MODULE_XJT(FAILSAFE_ROWS(EXTERNAL_MODULE)), LABEL(Trainer), 0, TRAINER_CHANNELS_ROWS(), IF_TRAINER_ON(2)}); -#elif defined(CPUARM) - #define IF_EXTERNAL_MODULE_XJT(x) (IS_MODULE_XJT(EXTERNAL_MODULE) ? (uint8_t)x : HIDDEN_ROW) - #define IF_EXTERNAL_MODULE_ON(x) (g_model.externalModule == MODULE_TYPE_NONE ? HIDDEN_ROW : (uint8_t)(x)) - #define IS_D8_RX(x) (g_model.moduleData[x].rfProtocol == RF_PROTO_D8) - #define EXTERNAL_MODULE_CHANNELS_ROWS() IF_EXTERNAL_MODULE_ON(IS_MODULE_DSM2(EXTERNAL_MODULE) ? (uint8_t)0 : (uint8_t)1) - #define TRAINER_CHANNELS_ROWS() (HIDDEN_ROW) - #define PORT_CHANNELS_ROWS(x) (x==EXTERNAL_MODULE ? EXTERNAL_MODULE_CHANNELS_ROWS() : 0) - #define FAILSAFE_ROWS(x) ((g_model.moduleData[x].rfProtocol==RF_PROTO_X16 || g_model.moduleData[x].rfProtocol==RF_PROTO_LR12) ? (g_model.moduleData[x].failsafeMode==FAILSAFE_CUSTOM ? (uint8_t)1 : (uint8_t)0) : HIDDEN_ROW) - #define CURSOR_ON_CELL (true) - #define MODEL_SETUP_MAX_LINES (1+ITEM_MODEL_SETUP_MAX) - #define POT_WARN_ITEMS() ((g_model.nPotsToWarn >> 6) ? (uint8_t)NUM_POTS : (uint8_t)0) - #define TIMER_ROWS 2, 0, CASE_PERSISTENT_TIMERS(0) 0, 0 -#if (defined(PCBSKY9X) && !defined(REVA) && !defined(REVX)) - #define EXTRA_MODULE_ROWS LABEL(ExtraModule), 1, 2, -#else - #define EXTRA_MODULE_ROWS -#endif - #define TRAINER_MODULE_ROWS - MENU_TAB({ 0, 0, TIMER_ROWS, TIMER_ROWS, TIMER_ROWS, 0, 1, 0, 0, CASE_PCBTARANIS(LABEL(Throttle)) 0, 0, 0, CASE_CPUARM(LABEL(PreflightCheck)) CASE_CPUARM(0) 0, 6, NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS-1, 0, LABEL(ExternalModule), (IS_MODULE_XJT(EXTERNAL_MODULE) || IS_MODULE_DSM2(EXTERNAL_MODULE)) ? (uint8_t)1 : (uint8_t)0, EXTERNAL_MODULE_CHANNELS_ROWS(), (IS_MODULE_XJT(EXTERNAL_MODULE) && IS_D8_RX(EXTERNAL_MODULE)) ? (uint8_t)1 : (IS_MODULE_PPM(EXTERNAL_MODULE) || IS_MODULE_XJT(EXTERNAL_MODULE) || IS_MODULE_DSM2(EXTERNAL_MODULE)) ? (uint8_t)2 : HIDDEN_ROW, IF_EXTERNAL_MODULE_XJT(FAILSAFE_ROWS(EXTERNAL_MODULE)), EXTRA_MODULE_ROWS TRAINER_MODULE_ROWS }); -#elif defined(CPUM64) - #define CURSOR_ON_CELL (true) - #define MODEL_SETUP_MAX_LINES ((IS_PPM_PROTOCOL(protocol)||IS_DSM2_PROTOCOL(protocol)||IS_PXX_PROTOCOL(protocol)) ? 1+ITEM_MODEL_SETUP_MAX : ITEM_MODEL_SETUP_MAX) - uint8_t protocol = g_model.protocol; - MENU_TAB({ 0, 0, CASE_PCBTARANIS(0) 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS-1, FIELD_PROTOCOL_MAX, 2 }); -#else - #define CURSOR_ON_CELL (true) - #define MODEL_SETUP_MAX_LINES ((IS_PPM_PROTOCOL(protocol)||IS_DSM2_PROTOCOL(protocol)||IS_PXX_PROTOCOL(protocol)) ? 1+ITEM_MODEL_SETUP_MAX : ITEM_MODEL_SETUP_MAX) - uint8_t protocol = g_model.protocol; - MENU_TAB({ 0, 0, CASE_PCBTARANIS(0) 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 0, 1, 0, 0, 0, 0, 0, NUM_SWITCHES, NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS-1, FIELD_PROTOCOL_MAX, 2, CASE_PCBSKY9X(1) CASE_PCBSKY9X(2) }); -#endif - - MENU_CHECK(menuTabModel, e_ModelSetup, MODEL_SETUP_MAX_LINES); - -#if defined(CPUARM) && (defined(DSM2) || defined(PXX)) - if (menuEvent) { - moduleFlag[0] = 0; -#if NUM_MODULES > 1 - moduleFlag[1] = 0; -#endif - } -#endif - - TITLE(STR_MENUSETUP); - - uint8_t sub = m_posVert - 1; - int8_t editMode = s_editMode; - - for (uint8_t i=0; i0) ? BLINK|INVERS : INVERS); - uint8_t attr = (sub == k ? blink : 0); - - switch(k) { - case ITEM_MODEL_NAME: - editSingleName(MODEL_SETUP_2ND_COLUMN, y, STR_MODELNAME, g_model.header.name, sizeof(g_model.header.name), event, attr); -#if defined(CPUARM) - memcpy(modelHeaders[g_eeGeneral.currModel].name, g_model.header.name, sizeof(g_model.header.name)); -#endif - break; - -#if defined(PCBTARANIS) && defined(SDCARD) - case ITEM_MODEL_BITMAP: - lcd_putsLeft(y, STR_BITMAP); - if (ZEXIST(g_model.header.bitmap)) - lcd_putsnAtt(MODEL_SETUP_2ND_COLUMN, y, g_model.header.bitmap, sizeof(g_model.header.bitmap), attr); - else - lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_VCSWFUNC, 0, attr); - if (attr && event==EVT_KEY_BREAK(KEY_ENTER) && READ_ONLY_UNLOCKED()) { - s_editMode = 0; - if (listSdFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap), g_model.header.bitmap, LIST_NONE_SD_FILE)) { - menuHandler = onModelSetupBitmapMenu; - } - else { - POPUP_WARNING(STR_NO_BITMAPS_ON_SD); - s_menu_flags = 0; - } - } - break; -#endif - -#if defined(CPUARM) - case ITEM_MODEL_TIMER1: - case ITEM_MODEL_TIMER2: - case ITEM_MODEL_TIMER3: - { - unsigned int timerIdx = (k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)); - TimerData * timer = &g_model.timers[timerIdx]; - putsStrIdx(0*FW, y, STR_TIMER, timerIdx+1); - putsTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, m_posHorz==0 ? attr : 0); - putsTimer(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, m_posHorz==1 ? attr : 0, m_posHorz==2 ? attr : 0); -#if defined(PCBTARANIS) - if (attr && m_posHorz < 0) lcd_filled_rect(MODEL_SETUP_2ND_COLUMN, y, LCD_W-MODEL_SETUP_2ND_COLUMN-MENUS_SCROLLBAR_WIDTH, 8); -#endif - if (attr && (editMode>0 || p1valdiff)) { - div_t qr = div(timer->start, 60); - switch (m_posHorz) { - case 0: - CHECK_INCDEC_MODELVAR_CHECK(event, timer->mode, SWSRC_FIRST, TMRMODE_COUNT+SWSRC_LAST-1/*SWSRC_None removed*/, isSwitchAvailableInTimers); - break; - case 1: - CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 59); - timer->start = qr.rem + qr.quot*60; - break; - case 2: - qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62)-2; - timer->start -= qr.rem ; - if ((int16_t)timer->start < 0) timer->start=0; - break; - } - } - break; - } - - case ITEM_MODEL_TIMER1_NAME: - case ITEM_MODEL_TIMER2_NAME: - case ITEM_MODEL_TIMER3_NAME: - { - TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)]; - editSingleName(MODEL_SETUP_2ND_COLUMN, y, STR_TIMER_NAME, timer->name, sizeof(timer->name), event, attr); - break; - } - - case ITEM_MODEL_TIMER1_MINUTE_BEEP: - case ITEM_MODEL_TIMER2_MINUTE_BEEP: - case ITEM_MODEL_TIMER3_MINUTE_BEEP: - { - TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)]; - timer->minuteBeep = onoffMenuItem(timer->minuteBeep, MODEL_SETUP_2ND_COLUMN, y, STR_MINUTEBEEP, attr, event); - break; - } - - case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP: - case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP: - case ITEM_MODEL_TIMER3_COUNTDOWN_BEEP: - { - TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)]; - timer->countdownBeep = selectMenuItem(MODEL_SETUP_2ND_COLUMN, y, STR_BEEPCOUNTDOWN, STR_VBEEPCOUNTDOWN, timer->countdownBeep, 0, 2, attr, event); - break; - } - - case ITEM_MODEL_TIMER1_PERSISTENT: - case ITEM_MODEL_TIMER2_PERSISTENT: - case ITEM_MODEL_TIMER3_PERSISTENT: - { - TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)]; - timer->persistent = selectMenuItem(MODEL_SETUP_2ND_COLUMN, y, STR_PERSISTENT, STR_VPERSISTENT, timer->persistent, 0, 2, attr, event); - break; - } -#else - case ITEM_MODEL_TIMER1: - case ITEM_MODEL_TIMER2: - case ITEM_MODEL_TIMER1_MINUTE_BEEP: - case ITEM_MODEL_TIMER2_MINUTE_BEEP: - case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP: - case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP: - { - TimerData *timer = &g_model.timers[k>=ITEM_MODEL_TIMER2 ? 1 : 0]; - if (k==ITEM_MODEL_TIMER1_MINUTE_BEEP || k==ITEM_MODEL_TIMER2_MINUTE_BEEP) { - timer->minuteBeep = onoffMenuItem(timer->minuteBeep, MODEL_SETUP_2ND_COLUMN, y, STR_MINUTEBEEP, attr, event); - } - else if (k==ITEM_MODEL_TIMER1_COUNTDOWN_BEEP || k==ITEM_MODEL_TIMER2_COUNTDOWN_BEEP) { - timer->countdownBeep = onoffMenuItem(timer->countdownBeep, MODEL_SETUP_2ND_COLUMN, y, STR_BEEPCOUNTDOWN, attr, event); - } - else { - putsStrIdx(0*FW, y, STR_TIMER, k>=ITEM_MODEL_TIMER2 ? 2 : 1); - putsTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, m_posHorz==0 ? attr : 0); - putsTimer(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, m_posHorz==1 ? attr : 0, m_posHorz==2 ? attr : 0); - if (attr && (editMode>0 || p1valdiff)) { - div_t qr = div(timer->start, 60); - switch (m_posHorz) { - case 0: - CHECK_INCDEC_MODELVAR_CHECK(event, timer->mode, SWSRC_FIRST, TMRMODE_COUNT+SWSRC_LAST-1/*SWSRC_None removed*/, isSwitchAvailableInTimers); - break; - case 1: - CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 59); - timer->start = qr.rem + qr.quot*60; - break; - case 2: - qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62)-2; - timer->start -= qr.rem ; - break; - } - } - } - break; - } - -#if defined(PCBGRUVIN9X) - case ITEM_MODEL_TIMER1_PERSISTENT: - case ITEM_MODEL_TIMER2_PERSISTENT: - { - TimerData &timer = g_model.timers[k==ITEM_MODEL_TIMER2_PERSISTENT]; - timer.persistent = selectMenuItem(MODEL_SETUP_2ND_COLUMN, y, STR_PERSISTENT, STR_VPERSISTENT, timer.persistent, 0, 2, attr, event); - break; - } -#endif -#endif - - case ITEM_MODEL_EXTENDED_LIMITS: - ON_OFF_MENU_ITEM(g_model.extendedLimits, MODEL_SETUP_2ND_COLUMN, y, STR_ELIMITS, attr, event); - break; - - case ITEM_MODEL_EXTENDED_TRIMS: -#if defined(CPUM64) - ON_OFF_MENU_ITEM(g_model.extendedTrims, MODEL_SETUP_2ND_COLUMN, y, STR_ETRIMS, attr, event); -#else - ON_OFF_MENU_ITEM(g_model.extendedTrims, MODEL_SETUP_2ND_COLUMN, y, STR_ETRIMS, m_posHorz<=0 ? attr : 0, event==EVT_KEY_BREAK(KEY_ENTER) ? event : 0); - lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_RESET_BTN, m_posHorz>0 && !s_noHi ? attr : 0); - if (attr && m_posHorz>0) { - s_editMode = 0; - if (event==EVT_KEY_LONG(KEY_ENTER)) { - s_noHi = NO_HI_LEN; - for (uint8_t i=0; i MIXSRC_Thr) - idx += 1; - if (idx >= MIXSRC_FIRST_POT+NUM_POTS) - idx += MIXSRC_CH1 - MIXSRC_FIRST_POT - NUM_POTS; - putsMixerSource(MODEL_SETUP_2ND_COLUMN, y, idx, attr); - break; - } - - case ITEM_MODEL_THROTTLE_TRIM: - ON_OFF_MENU_ITEM(g_model.thrTrim, MODEL_SETUP_2ND_COLUMN, y, STR_TTRIM, attr, event); - break; - -#if defined(CPUARM) - case ITEM_MODEL_PREFLIGHT_LABEL: - lcd_putsLeft(y, STR_PREFLIGHT); - break; - - case ITEM_MODEL_CHECKLIST_DISPLAY: - ON_OFF_MENU_ITEM(g_model.displayChecklist, MODEL_SETUP_2ND_COLUMN, y, STR_CHECKLIST, attr, event); - break; -#endif - - case ITEM_MODEL_THROTTLE_WARNING: - g_model.disableThrottleWarning = !onoffMenuItem(!g_model.disableThrottleWarning, MODEL_SETUP_2ND_COLUMN, y, STR_THROTTLEWARNING, attr, event); - break; - -#if defined(PCBTARANIS) - // TODO something more generic - case ITEM_MODEL_SWITCHES_WARNING2: - if (i==0) s_pgOfs++; - break; -#endif - - case ITEM_MODEL_SWITCHES_WARNING: -#if defined(PCBTARANIS) - // TODO something more generic - if (i==LCD_LINES-2) { - s_pgOfs++; - break; - } -#endif - { - lcd_putsLeft(y, STR_SWITCHWARNING); - swarnstate_t states = g_model.switchWarningState; - char c; - if (attr) { - s_editMode = 0; - if (!READ_ONLY()) { - switch (event) { - CASE_EVT_ROTARY_BREAK - case EVT_KEY_BREAK(KEY_ENTER): -#if defined(CPUM64) - g_model.switchWarningEnable ^= (1 << m_posHorz); - eeDirty(EE_MODEL); -#elif !defined(PCBTARANIS) - if (m_posHorz < NUM_SWITCHES-1) { - g_model.switchWarningEnable ^= (1 << m_posHorz); - eeDirty(EE_MODEL); - } -#endif - break; - - case EVT_KEY_LONG(KEY_ENTER): -#if defined(CPUM64) - getMovedSwitch(); - g_model.switchWarningState = switches_states; - AUDIO_WARNING1(); - eeDirty(EE_MODEL); -#elif defined(PCBTARANIS) - if (m_posHorz < 0) { - s_noHi = NO_HI_LEN; - getMovedSwitch(); - g_model.switchWarningState = switches_states; - AUDIO_WARNING1(); - eeDirty(EE_MODEL); - } -#else - if (m_posHorz == NUM_SWITCHES-1) { - s_noHi = NO_HI_LEN; - getMovedSwitch(); - g_model.switchWarningState = switches_states; - AUDIO_WARNING1(); - eeDirty(EE_MODEL); - } -#endif - killEvents(event); - break; - } - } - } - - LcdFlags line = attr; - -#if defined(PCBTARANIS) - for (int i=0, current=0; i>= 2; - } - if (attr && m_posHorz < 0) { - lcd_filled_rect(MODEL_SETUP_2ND_COLUMN-1, y-1, 8*(2*FW+1), ONE_2x2POS_DEFINED() ? 2*FH+1 : FH+1); - } -#else - for (uint8_t i=0; i>= 2; - } - else { - if ((states & 0x01) && swactive) - attr = INVERS; - c = pgm_read_byte(STR_VSWITCHES - 2 + 9 + (3*(i+1))); - states >>= 1; - } - if (line && (m_posHorz == i)) { - attr = BLINK; - if (swactive) - attr |= INVERS; - } - lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+i*FW, y, (swactive || (attr & BLINK)) ? c : '-', attr); -#if !defined(CPUM64) - lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+(NUM_SWITCHES*FW), y, PSTR("<]"), (m_posHorz == NUM_SWITCHES-1 && !s_noHi) ? line : 0); -#endif - } -#endif - break; - } - -#if defined(PCBTARANIS) - case ITEM_MODEL_POT_WARNING: - { - lcd_putsLeft(y, STR_POTWARNING); - uint8_t potMode = g_model.nPotsToWarn >> 6; - if (attr) { - if (m_posHorz) s_editMode = 0; - if (!READ_ONLY() && m_posHorz) { - switch (event) { - case EVT_KEY_LONG(KEY_ENTER): - killEvents(event); - if (potMode == 1) { - SAVE_POT_POSITION(m_posHorz-1); - AUDIO_WARNING1(); - eeDirty(EE_MODEL); - } - break; - case EVT_KEY_BREAK(KEY_ENTER): - g_model.nPotsToWarn ^= (1 << (m_posHorz-1)); - eeDirty(EE_MODEL); - break; - } - } - } - lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, PSTR("\004""OFF\0""Man\0""Auto"), potMode, attr & ((m_posHorz == 0) ? attr : !INVERS)); - if (potMode) { - coord_t x = MODEL_SETUP_2ND_COLUMN+5*FW; - for (uint8_t i=0; i POT3) { - x -= FW; - } -#endif - lcd_putsiAtt(x, y, STR_RETA123, i, ((m_posHorz==i) && attr) ? BLINK|INVERS : (((g_model.beepANACenter & ((BeepANACenter)1<0 || p1valdiff)) { - switch (m_posHorz) { - case 0: - g_model.externalModule = checkIncDec(event, g_model.externalModule, MODULE_TYPE_NONE, MODULE_TYPE_COUNT-1, EE_MODEL, isModuleAvailable); - if (checkIncDec_Ret) { - g_model.moduleData[EXTERNAL_MODULE].rfProtocol = 0; - g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0; - if (g_model.externalModule == MODULE_TYPE_PPM) - g_model.moduleData[EXTERNAL_MODULE].channelsCount = 0; - else - g_model.moduleData[EXTERNAL_MODULE].channelsCount = MAX_EXTERNAL_MODULE_CHANNELS(); - } - break; - case 1: - if (IS_MODULE_DSM2(EXTERNAL_MODULE)) - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, DSM2_PROTO_LP45, DSM2_PROTO_DSMX); - else - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, RF_PROTO_X16, RF_PROTO_LAST); - if (checkIncDec_Ret) { - g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0; - g_model.moduleData[EXTERNAL_MODULE].channelsCount = MAX_EXTERNAL_MODULE_CHANNELS(); - } - } - } - break; -#endif - -#if defined(PCBTARANIS) - case ITEM_MODEL_TRAINER_LABEL: - lcd_putsLeft(y, STR_TRAINER); - break; - - case ITEM_MODEL_INTERNAL_MODULE_CHANNELS: -#elif defined(PCBSKY9X) && !defined(REVX) - case ITEM_MODEL_EXTRA_MODULE_CHANNELS: -#endif -#if defined(CPUARM) - case ITEM_MODEL_EXTERNAL_MODULE_CHANNELS: -#if defined(PCBTARANIS) - case ITEM_MODEL_TRAINER_CHANNELS: -#endif - { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); - ModuleData & moduleData = g_model.moduleData[moduleIdx]; - lcd_putsLeft(y, STR_CHANNELRANGE); - if ((int8_t)PORT_CHANNELS_ROWS(moduleIdx) >= 0) { - lcd_putsAtt(MODEL_SETUP_2ND_COLUMN, y, STR_CH, m_posHorz==0 ? attr : 0); - lcd_outdezAtt(lcdLastPos, y, moduleData.channelsStart+1, LEFT | (m_posHorz==0 ? attr : 0)); - lcd_putc(lcdLastPos, y, '-'); - lcd_outdezAtt(lcdLastPos + FW+1, y, moduleData.channelsStart+NUM_CHANNELS(moduleIdx), LEFT | (m_posHorz==1 ? attr : 0)); - if (attr && (editMode>0 || p1valdiff)) { - switch (m_posHorz) { - case 0: - CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.channelsStart, 32-8-moduleData.channelsCount); - break; - case 1: - CHECK_INCDEC_MODELVAR(event, moduleData.channelsCount, -4, min(MAX_CHANNELS(moduleIdx), 32-8-moduleData.channelsStart)); - if ((k == ITEM_MODEL_EXTERNAL_MODULE_CHANNELS && g_model.externalModule == MODULE_TYPE_PPM) -#if defined(PCBTARANIS) - || (k == ITEM_MODEL_TRAINER_CHANNELS) -#endif - ) - SET_DEFAULT_PPM_FRAME_LENGTH(moduleIdx); - break; - } - } - } - break; - } -#endif - -#if defined(PCBTARANIS) - case ITEM_MODEL_INTERNAL_MODULE_BIND: -#elif defined(PCBSKY9X) && !defined(REVX) - case ITEM_MODEL_EXTRA_MODULE_BIND: -#endif -#if defined(CPUARM) - case ITEM_MODEL_EXTERNAL_MODULE_BIND: -#if defined(PCBTARANIS) - case ITEM_MODEL_TRAINER_SETTINGS: -#endif - { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); - ModuleData & moduleData = g_model.moduleData[moduleIdx]; - if (IS_MODULE_PPM(moduleIdx)) { - lcd_putsLeft(y, STR_PPMFRAME); - lcd_puts(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS); - lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppmFrameLength*5 + 225, (m_posHorz<=0 ? attr : 0) | PREC1|LEFT); - lcd_putc(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u'); - lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (moduleData.ppmDelay*50)+300, (CURSOR_ON_LINE() || m_posHorz==1) ? attr : 0); - lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+10*FW, y, moduleData.ppmPulsePol ? '+' : '-', (CURSOR_ON_LINE() || m_posHorz==2) ? attr : 0); - - if (attr && (editMode>0 || p1valdiff)) { - switch (m_posHorz) { - case 0: - CHECK_INCDEC_MODELVAR(event, moduleData.ppmFrameLength, -20, 35); - break; - case 1: - CHECK_INCDEC_MODELVAR(event, moduleData.ppmDelay, -4, 10); - break; - case 2: - CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.ppmPulsePol, 1); - break; - } - } - } - else { - horzpos_t l_posHorz = m_posHorz; - coord_t xOffsetBind = MODEL_SETUP_BIND_OFS; - if (IS_MODULE_XJT(moduleIdx) && IS_D8_RX(moduleIdx)) { - xOffsetBind = 0; - lcd_putsLeft(y, INDENT "Receiver"); - if (attr) l_posHorz += 1; - } - else { - lcd_putsLeft(y, STR_RXNUM); - } - if (IS_MODULE_XJT(moduleIdx) || IS_MODULE_DSM2(moduleIdx)) { - if (xOffsetBind) lcd_outdezNAtt(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId, (l_posHorz==0 ? attr : 0) | LEADING0|LEFT, 2); - if (attr && l_posHorz==0) { - if (editMode>0 || p1valdiff) { - CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId, IS_MODULE_DSM2(moduleIdx) ? 20 : 63); - if (checkIncDec_Ret) { - modelHeaders[g_eeGeneral.currModel].modelId = g_model.header.modelId; - } - } - if (editMode==0 && event==EVT_KEY_BREAK(KEY_ENTER)) { - checkModelIdUnique(g_eeGeneral.currModel); - } - } - lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+xOffsetBind, y, STR_MODULE_BIND, l_posHorz==1 ? attr : 0); - lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+MODEL_SETUP_RANGE_OFS+xOffsetBind, y, STR_MODULE_RANGE, l_posHorz==2 ? attr : 0); - uint8_t newFlag = 0; - if (attr && l_posHorz>0 && s_editMode>0) { - if (l_posHorz == 1) - newFlag = MODULE_BIND; - else if (l_posHorz == 2) { - newFlag = MODULE_RANGECHECK; - } - } - moduleFlag[moduleIdx] = newFlag; - } - } - break; - } -#endif - -#if defined(PCBTARANIS) - case ITEM_MODEL_INTERNAL_MODULE_FAILSAFE: -#endif -#if defined(CPUARM) - case ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE: - { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); - ModuleData & moduleData = g_model.moduleData[moduleIdx]; - lcd_putsLeft(y, TR_FAILSAFE); - if (IS_MODULE_XJT(moduleIdx)) { - lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_VFAILSAFE, moduleData.failsafeMode, m_posHorz==0 ? attr : 0); - if (moduleData.failsafeMode == FAILSAFE_CUSTOM) lcd_putsAtt(MODEL_SETUP_2ND_COLUMN + MODEL_SETUP_SET_FAILSAFE_OFS, y, STR_SET, m_posHorz==1 ? attr : 0); - if (attr) { - if (moduleData.failsafeMode != FAILSAFE_CUSTOM) - m_posHorz = 0; - if (m_posHorz==0) { - if (editMode>0 || p1valdiff) { - CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.failsafeMode, FAILSAFE_LAST); - if (checkIncDec_Ret) SEND_FAILSAFE_NOW(moduleIdx); - } - } - else if (m_posHorz==1) { - s_editMode = 0; - if (moduleData.failsafeMode==FAILSAFE_CUSTOM && event==EVT_KEY_FIRST(KEY_ENTER)) { - g_moduleIdx = moduleIdx; - pushMenu(menuModelFailsafe); - } - } - else { - lcd_filled_rect(MODEL_SETUP_2ND_COLUMN, y, LCD_W-MODEL_SETUP_2ND_COLUMN-MENUS_SCROLLBAR_WIDTH, 8); - } - } - } - break; - } -#endif - -#if !defined(CPUARM) - case ITEM_MODEL_PPM1_PROTOCOL: - lcd_putsLeft(y, NO_INDENT(STR_PROTO)); - lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_VPROTOS, protocol, m_posHorz<=0 ? attr : 0); - if (IS_PPM_PROTOCOL(protocol)) { - lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN+7*FW, y, STR_NCHANNELS, g_model.ppmNCH+2, m_posHorz!=0 ? attr : 0); - } - else if (m_posHorz>0 && attr) { - MOVE_CURSOR_FROM_HERE(); - } - if (attr && (editMode>0 || p1valdiff || (!IS_PPM_PROTOCOL(protocol) && !IS_DSM2_PROTOCOL(protocol)))) { - switch (m_posHorz) { - case 0: - CHECK_INCDEC_MODELVAR_ZERO(event, g_model.protocol, PROTO_MAX-1); - break; - case 1: - CHECK_INCDEC_MODELVAR(event, g_model.ppmNCH, -2, 4); - g_model.ppmFrameLength = g_model.ppmNCH * 8; - break; - } - } - break; -#endif - -#if 0 - case ITEM_MODEL_PPM2_PROTOCOL: - lcd_putsLeft(y, PSTR("Port2")); - lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_VPROTOS, 0, 0); - lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+4*FW+3, y, STR_CH, m_posHorz<=0 ? attr : 0); - lcd_outdezAtt(lcdLastPos, y, g_model.moduleData[1].channelsStart+1, LEFT | (m_posHorz<=0 ? attr : 0)); - lcd_putc(lcdLastPos, y, '-'); - lcd_outdezAtt(lcdLastPos + FW+1, y, g_model.moduleData[1].channelsStart+8+g_model.moduleData[1].channelsCount, LEFT | (m_posHorz!=0 ? attr : 0)); - if (attr && (editMode>0 || p1valdiff)) { - switch (m_posHorz) { - case 0: - CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[1].channelsStart, 32-8-g_model.moduleData[1].channelsCount); - SET_DEFAULT_PPM_FRAME_LENGTH(1); - break; - case 1: - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].channelsCount, -4, min(8, 32-8-g_model.moduleData[1].channelsStart)); - SET_DEFAULT_PPM_FRAME_LENGTH(1); - break; - } - } - break; - - case ITEM_MODEL_PPM2_PARAMS: - lcd_putsLeft(y, STR_PPMFRAME); - lcd_puts(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS); - lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.moduleData[1].ppmFrameLength*5 + 225, (m_posHorz<=0 ? attr : 0) | PREC1|LEFT); - lcd_putc(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u'); - lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.moduleData[1].ppmDelay*50)+300, (m_posHorz < 0 || m_posHorz==1) ? attr : 0); - lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.moduleData[1].ppmPulsePol ? '+' : '-', (m_posHorz < 0 || m_posHorz==2) ? attr : 0); - if (attr && (editMode>0 || p1valdiff)) { - switch (m_posHorz) { - case 0: - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].ppmFrameLength, -20, 35); - break; - case 1: - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].ppmDelay, -4, 10); - break; - case 2: - CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[1].ppmPulsePol, 1); - break; - } - } - break; -#endif - -#if !defined(CPUARM) - case ITEM_MODEL_PPM1_PARAMS: - if (IS_PPM_PROTOCOL(protocol)) { - lcd_putsLeft(y, STR_PPMFRAME); - lcd_puts(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS); - lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.ppmFrameLength*5 + 225, (m_posHorz<=0 ? attr : 0) | PREC1|LEFT); - lcd_putc(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u'); - lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.ppmDelay*50)+300, (CURSOR_ON_LINE() || m_posHorz==1) ? attr : 0); - lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.pulsePol ? '+' : '-', (CURSOR_ON_LINE() || m_posHorz==2) ? attr : 0); - if (attr && (editMode>0 || p1valdiff)) { - switch (m_posHorz) { - case 0: - CHECK_INCDEC_MODELVAR(event, g_model.ppmFrameLength, -20, 35); - break; - case 1: - CHECK_INCDEC_MODELVAR(event, g_model.ppmDelay, -4, 10); - break; - case 2: - CHECK_INCDEC_MODELVAR_ZERO(event, g_model.pulsePol, 1); - break; - } - } - } -#if defined(DSM2) || defined(PXX) - else if (IS_DSM2_PROTOCOL(protocol) || IS_PXX_PROTOCOL(protocol)) { - if (attr && m_posHorz > 1) { - REPEAT_LAST_CURSOR_MOVE(); // limit 3 column row to 2 colums (Rx_Num and RANGE fields) - } - - lcd_putsLeft(y, STR_RXNUM); - lcd_outdezNAtt(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId, (m_posHorz<=0 ? attr : 0) | LEADING0|LEFT, 2); - if (attr && (m_posHorz==0 && (editMode>0 || p1valdiff))) { - CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId, 99); - } -#if defined(PXX) - if (protocol == PROTO_PXX) { - lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_SYNCMENU, m_posHorz!=0 ? attr : 0); - uint8_t newFlag = 0; - if (attr && m_posHorz>0 && editMode>0) { - // send reset code - newFlag = MODULE_BIND; - } - moduleFlag[0] = newFlag; - } -#endif -#if defined(DSM2) - if (IS_DSM2_PROTOCOL(protocol)) { - lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_MODULE_RANGE, m_posHorz!=0 ? attr : 0); - moduleFlag[0] = (attr && m_posHorz>0 && editMode>0) ? MODULE_RANGECHECK : 0; // [MENU] key toggles range check mode - } -#endif - } -#endif - break; -#endif - } - } - -#if defined(CPUARM) && defined(PXX) - if (IS_RANGECHECK_ENABLE()) { - displayPopup("RSSI: "); - lcd_outdezAtt(16+4*FW, 5*FH, TELEMETRY_RSSI(), BOLD); - } -#endif -} - static uint8_t s_currIdx; -#if MENU_COLUMNS < 2 - #if LCD_W >= 212 - #define MIXES_2ND_COLUMN (18*FW) - #else - #define MIXES_2ND_COLUMN (12*FW) - #endif -#else - #define MIXES_2ND_COLUMN (9*FW) -#endif - -#if LCD_W >= 212 - #define EXPO_ONE_2ND_COLUMN (LCD_W-8*FW-90) - #define EXPO_ONE_FP_WIDTH (9*FW) -#else - #define EXPO_ONE_2ND_COLUMN (7*FW+3*FW+2) - #define EXPO_ONE_FP_WIDTH (5*FW) -#endif - -#if MENU_COLUMNS > 1 -uint8_t editDelay(const coord_t x, const coord_t y, const uint8_t event, const uint8_t attr, const pm_char *str, uint8_t delay) -{ - lcd_puts(x, y, str); - lcd_outdezAtt(x+MIXES_2ND_COLUMN, y, (10/DELAY_STEP)*delay, attr|PREC1|LEFT); - if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, delay, DELAY_MAX); - return delay; -} -#define EDIT_DELAY(x, y, event, attr, str, delay) editDelay(x, y, event, attr, str, delay) -#else -uint8_t editDelay(const coord_t y, const uint8_t event, const uint8_t attr, const pm_char *str, uint8_t delay) -{ - lcd_putsLeft(y, str); - lcd_outdezAtt(MIXES_2ND_COLUMN, y, (10/DELAY_STEP)*delay, attr|PREC1|LEFT); - if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, delay, DELAY_MAX); - return delay; -} -#define EDIT_DELAY(x, y, event, attr, str, delay) editDelay(y, event, attr, str, delay) -#endif - -#if defined(FLIGHT_MODES) - -#if defined(CPUARM) - #define FlightModesType uint16_t -#else - #define FlightModesType uint8_t -#endif - -#if defined(PCBTARANIS) -void displayFlightModes(coord_t x, coord_t y, FlightModesType value) -{ - lcd_puts(x, y, STR_FP); - x = lcdNextPos + 1; - for (uint8_t p=0; p EXPO_ONE_2ND_COLUMN-FW))) - continue; -#endif -#if defined(PCBTARANIS) - LcdFlags flags = 0; - if (attr) { - flags |= INVERS; - if (posHorz==p) flags |= BLINK; - } - if (value & (1< 0) { posHorz += 1; } - - if (sub=0) { - displayColumnHeader(STR_PHASES_HEADERS, posHorz); - } - - for (uint8_t i=0; i0) ? BLINK|INVERS : INVERS) : 0); - uint8_t active = (attr && (s_editMode>0 || p1valdiff)) ; - switch (j) { - case ITEM_FLIGHT_MODES_NAME: - editName(4*FW-1, y, p->name, sizeof(p->name), event, attr); - break; - - case ITEM_FLIGHT_MODES_SWITCH: - putsSwitches((5+LEN_FLIGHT_MODE_NAME)*FW+FW/2, y, p->swtch, attr); - if (active) CHECK_INCDEC_MODELSWITCH(event, p->swtch, SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, isSwitchAvailableInMixes); - break; - - case ITEM_FLIGHT_MODES_TRIM_RUD: - case ITEM_FLIGHT_MODES_TRIM_ELE: - case ITEM_FLIGHT_MODES_TRIM_THR: - case ITEM_FLIGHT_MODES_TRIM_AIL: - { - uint8_t t = j-ITEM_FLIGHT_MODES_TRIM_RUD; - putsTrimMode((4+LEN_FLIGHT_MODE_NAME)*FW+j*(5*FW/2), y, k, t, attr); - if (active) { - trim_t & v = p->trim[t]; - v.mode = checkIncDec(event, v.mode==TRIM_MODE_NONE ? -1 : v.mode, -1, k==0 ? 0 : 2*MAX_FLIGHT_MODES-1, EE_MODEL, isTrimModeAvailable); - } - break; - } - - case ITEM_FLIGHT_MODES_FADE_IN: - lcd_outdezAtt(32*FW-2, y, (10/DELAY_STEP)*p->fadeIn, attr|PREC1); - if (active) p->fadeIn = checkIncDec(event, p->fadeIn, 0, DELAY_MAX, EE_MODEL|NO_INCDEC_MARKS); - break; - - case ITEM_FLIGHT_MODES_FADE_OUT: - lcd_outdezAtt(35*FW, y, (10/DELAY_STEP)*p->fadeOut, attr|PREC1); - if (active) p->fadeOut = checkIncDec(event, p->fadeOut, 0, DELAY_MAX, EE_MODEL|NO_INCDEC_MARKS); - break; - - } - } - } -} - -#else // PCBTARANIS - -enum menuModelPhaseItems { - ITEM_MODEL_PHASE_NAME, - ITEM_MODEL_PHASE_SWITCH, - ITEM_MODEL_PHASE_TRIMS, - IF_ROTARY_ENCODERS(ITEM_MODEL_PHASE_ROTARY_ENCODERS) - ITEM_MODEL_PHASE_FADE_IN, - ITEM_MODEL_PHASE_FADE_OUT, -#if defined(GVARS) && !defined(PCBSTD) - ITEM_MODEL_PHASE_GVARS_LABEL, - ITEM_MODEL_PHASE_GV1, - ITEM_MODEL_PHASE_GV2, - ITEM_MODEL_PHASE_GV3, - ITEM_MODEL_PHASE_GV4, - ITEM_MODEL_PHASE_GV5, -#if defined(CPUARM) - ITEM_MODEL_PHASE_GV6, - ITEM_MODEL_PHASE_GV7, - ITEM_MODEL_PHASE_GV8, - ITEM_MODEL_PHASE_GV9, -#endif -#endif - ITEM_MODEL_PHASE_MAX -}; - -void menuModelPhaseOne(uint8_t event) -{ - FlightModeData *fm = flightModeAddress(s_currIdx); - putsFlightMode(13*FW, 0, s_currIdx+1, (getFlightMode()==s_currIdx ? BOLD : 0)); - -#if defined(GVARS) && !defined(PCBSTD) - static const pm_uint8_t mstate_tab_fm1[] PROGMEM = {0, 0, 0, (uint8_t)-1, 1, 1, 1, 1, 1}; - static const pm_uint8_t mstate_tab_others[] PROGMEM = {0, 0, 3, IF_ROTARY_ENCODERS(NUM_ROTARY_ENCODERS-1) 0, 0, (uint8_t)-1, 2, 2, 2, 2, 2}; - - check(event, 0, NULL, 0, (s_currIdx == 0) ? mstate_tab_fm1 : mstate_tab_others, DIM(mstate_tab_others)-1, ITEM_MODEL_PHASE_MAX - 1 - (s_currIdx==0 ? (ITEM_MODEL_PHASE_FADE_IN-ITEM_MODEL_PHASE_SWITCH) : 0)); - - TITLE(STR_MENUFLIGHTPHASE); - - #define PHASE_ONE_FIRST_LINE (1+1*FH) -#else - SUBMENU(STR_MENUFLIGHTPHASE, 3 + (s_currIdx==0 ? 0 : 2 + (bool)NUM_ROTARY_ENCODERS), {0, 0, 3, IF_ROTARY_ENCODERS(NUM_ROTARY_ENCODERS-1) 0/*, 0*/}); - #define PHASE_ONE_FIRST_LINE (1+1*FH) -#endif - - int8_t sub = m_posVert; - int8_t editMode = s_editMode; - -#if defined(GVARS) && !defined(PCBSTD) - if (s_currIdx == 0 && sub>=ITEM_MODEL_PHASE_SWITCH) sub += ITEM_MODEL_PHASE_FADE_IN-ITEM_MODEL_PHASE_SWITCH; - - for (uint8_t k=0; k=ITEM_MODEL_PHASE_SWITCH) i += ITEM_MODEL_PHASE_FADE_IN-ITEM_MODEL_PHASE_SWITCH; - uint8_t attr = (sub==i ? (editMode>0 ? BLINK|INVERS : INVERS) : 0); -#else - for (uint8_t i=0, k=0, y=PHASE_ONE_FIRST_LINE; i0 ? BLINK|INVERS : INVERS) : 0); -#endif - switch(i) { - case ITEM_MODEL_PHASE_NAME: - editSingleName(MIXES_2ND_COLUMN, y, STR_PHASENAME, fm->name, sizeof(fm->name), event, attr); - break; - case ITEM_MODEL_PHASE_SWITCH: - fm->swtch = switchMenuItem(MIXES_2ND_COLUMN, y, fm->swtch, attr, event); - break; - case ITEM_MODEL_PHASE_TRIMS: - lcd_putsLeft(y, STR_TRIMS); - for (uint8_t t=0; t0) || p1valdiff)) { - int16_t v = getRawTrimValue(s_currIdx, t); - if (v < TRIM_EXTENDED_MAX) v = TRIM_EXTENDED_MAX; - v = checkIncDec(event, v, TRIM_EXTENDED_MAX, TRIM_EXTENDED_MAX+MAX_FLIGHT_MODES-1, EE_MODEL); - if (checkIncDec_Ret) { - if (v == TRIM_EXTENDED_MAX) v = 0; - setTrimValue(s_currIdx, t, v); - } - } - } - break; - -#if ROTARY_ENCODERS > 0 - case ITEM_MODEL_PHASE_ROTARY_ENCODERS: - lcd_putsLeft(y, STR_ROTARY_ENCODER); - for (uint8_t t=0; t0) || p1valdiff)) { - int16_t v = flightModeAddress(s_currIdx)->rotaryEncoders[t]; - if (v < ROTARY_ENCODER_MAX) v = ROTARY_ENCODER_MAX; - v = checkIncDec(event, v, ROTARY_ENCODER_MAX, ROTARY_ENCODER_MAX+MAX_FLIGHT_MODES-1, EE_MODEL); - if (checkIncDec_Ret) { - if (v == ROTARY_ENCODER_MAX) v = 0; - flightModeAddress(s_currIdx)->rotaryEncoders[t] = v; - } - } - } - break; -#endif - - case ITEM_MODEL_PHASE_FADE_IN: - fm->fadeIn = EDIT_DELAY(0, y, event, attr, STR_FADEIN, fm->fadeIn); - break; - - case ITEM_MODEL_PHASE_FADE_OUT: - fm->fadeOut = EDIT_DELAY(0, y, event, attr, STR_FADEOUT, fm->fadeOut); - break; - -#if defined(GVARS) && !defined(PCBSTD) - case ITEM_MODEL_PHASE_GVARS_LABEL: - lcd_putsLeft(y, STR_GLOBAL_VARS); - break; - - default: - { - uint8_t idx = i-ITEM_MODEL_PHASE_GV1; - uint8_t posHorz = m_posHorz; - if (attr && posHorz > 0 && s_currIdx==0) posHorz++; - - putsStrIdx(INDENT_WIDTH, y, STR_GV, idx+1); - - editName(4*FW, y, g_model.gvars[idx].name, LEN_GVAR_NAME, event, posHorz==0 ? attr : 0); - - int16_t v = fm->gvars[idx]; - if (v > GVAR_MAX) { - uint8_t p = v - GVAR_MAX - 1; - if (p >= s_currIdx) p++; - putsFlightMode(11*FW, y, p+1, posHorz==1 ? attr : 0); - } - else { - lcd_putsAtt(11*FW, y, STR_OWN, posHorz==1 ? attr : 0); - } - if (attr && s_currIdx>0 && posHorz==1 && (editMode>0 || p1valdiff)) { - if (v < GVAR_MAX) v = GVAR_MAX; - v = checkIncDec(event, v, GVAR_MAX, GVAR_MAX+MAX_FLIGHT_MODES-1, EE_MODEL); - if (checkIncDec_Ret) { - if (v == GVAR_MAX) v = 0; - fm->gvars[idx] = v; - } - } - - uint8_t p = getGVarFlightPhase(s_currIdx, idx); - lcd_outdezAtt(21*FW, y, GVAR_VALUE(idx, p), posHorz==2 ? attr : 0); - if (attr && posHorz==2 && ((editMode>0) || p1valdiff)) { - GVAR_VALUE(idx, p) = checkIncDec(event, GVAR_VALUE(idx, p), -GVAR_LIMIT, GVAR_LIMIT, EE_MODEL); - } - - break; - } -#endif - } - } -} - -#if defined(ROTARY_ENCODERS) - #if ROTARY_ENCODERS > 2 - #define NAME_OFS (-4-12) - #define SWITCH_OFS (-FW/2-2-13) - #define TRIMS_OFS (-FW/2-4-15) - #define ROTARY_ENC_OFS (0) - #else - #define NAME_OFS (-4) - #define SWITCH_OFS (-FW/2-2) - #define TRIMS_OFS (-FW/2-4) - #define ROTARY_ENC_OFS (2) - #endif -#else - #define NAME_OFS 0 - #define SWITCH_OFS (FW/2) - #define TRIMS_OFS (FW/2) -#endif - -void menuModelFlightModesAll(uint8_t event) -{ - SIMPLE_MENU(STR_MENUFLIGHTPHASES, menuTabModel, e_FlightModesAll, 1+MAX_FLIGHT_MODES+1); - - int8_t sub = m_posVert - 1; - - switch (event) { - CASE_EVT_ROTARY_BREAK - case EVT_KEY_FIRST(KEY_ENTER): - if (sub == MAX_FLIGHT_MODES) { - s_editMode = 0; - trimsCheckTimer = 200; // 2 seconds - } - // no break - case EVT_KEY_FIRST(KEY_RIGHT): - if (sub >= 0 && sub < MAX_FLIGHT_MODES) { - s_currIdx = sub; - pushMenu(menuModelPhaseOne); - } - break; - } - - uint8_t att; - for (uint8_t i=0; i(LCD_LINES-1)*FH+MENU_TITLE_HEIGHT-FH) continue; -#else - coord_t y = MENU_TITLE_HEIGHT + 1 + i*FH; -#endif - att = (i==sub ? INVERS : 0); - FlightModeData *p = flightModeAddress(i); - putsFlightMode(0, y, i+1, att|(getFlightMode()==i ? BOLD : 0)); - - lcd_putsnAtt(4*FW+NAME_OFS, y, p->name, sizeof(p->name), ZCHAR); - if (i == 0) { - lcd_puts((5+LEN_FLIGHT_MODE_NAME)*FW+SWITCH_OFS, y, STR_DEFAULT); - } - else { - putsSwitches((5+LEN_FLIGHT_MODE_NAME)*FW+SWITCH_OFS, y, p->swtch, 0); - for (uint8_t t=0; tfadeIn || p->fadeOut) - lcd_putc(LCD_W-FW-MENUS_SCROLLBAR_WIDTH, y, (p->fadeIn && p->fadeOut) ? '*' : (p->fadeIn ? 'I' : 'O')); - - } - -#if defined(CPUARM) - if (s_pgOfs != MAX_FLIGHT_MODES-(LCD_LINES-2)) return; -#endif - - lcd_putsLeft((LCD_LINES-1)*FH+1, STR_CHECKTRIMS); - putsFlightMode(OFS_CHECKTRIMS, (LCD_LINES-1)*FH+1, mixerCurrentFlightMode+1); - if (sub==MAX_FLIGHT_MODES && !trimsCheckTimer) { - lcd_status_line(); - } -} - -#endif // defined(PCBTARANIS) - -#else // defined(FLIGHT_MODES) - -#define displayFlightModes(...) - -#endif - -#if defined(HELI) - -enum menuModelHeliItems { - ITEM_HELI_SWASHTYPE, - ITEM_HELI_COLLECTIVE, - ITEM_HELI_SWASHRING, - ITEM_HELI_ELEDIRECTION, - ITEM_HELI_AILDIRECTION, - ITEM_HELI_COLDIRECTION -}; - -#if LCD_W >= 212 - #define HELI_PARAM_OFS (23*FW) -#else - #define HELI_PARAM_OFS (14*FW) -#endif - -void menuModelHeli(uint8_t event) -{ - SIMPLE_MENU(STR_MENUHELISETUP, menuTabModel, e_Heli, 7); - - uint8_t sub = m_posVert - 1; - - for (uint8_t i=0; i<6; i++) { - coord_t y = MENU_TITLE_HEIGHT + 1 + i*FH; - uint8_t attr = (sub == i ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0); - switch(i) { - case ITEM_HELI_SWASHTYPE: - g_model.swashR.type = selectMenuItem(HELI_PARAM_OFS, y, STR_SWASHTYPE, STR_VSWASHTYPE, g_model.swashR.type, 0, SWASH_TYPE_MAX, attr, event); - break; - - case ITEM_HELI_COLLECTIVE: -#if defined(PCBTARANIS) - lcd_putsLeft(y, STR_COLLECTIVE); - if (attr) CHECK_INCDEC_MODELSOURCE(event, g_model.swashR.collectiveSource, 0, MIXSRC_LAST_CH); -#else - g_model.swashR.collectiveSource = selectMenuItem(HELI_PARAM_OFS, y, STR_COLLECTIVE, NULL, g_model.swashR.collectiveSource, 0, MIXSRC_LAST_CH, attr, event); -#endif - putsMixerSource(HELI_PARAM_OFS, y, g_model.swashR.collectiveSource, attr); - break; - - case ITEM_HELI_SWASHRING: - lcd_putsLeft(y, STR_SWASHRING); - lcd_outdezAtt(HELI_PARAM_OFS, y, g_model.swashR.value, LEFT|attr); - if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.swashR.value, 100); - break; - - case ITEM_HELI_ELEDIRECTION: - g_model.swashR.invertELE = selectMenuItem(HELI_PARAM_OFS, y, STR_ELEDIRECTION, STR_MMMINV, g_model.swashR.invertELE, 0, 1, attr, event); - break; - - case ITEM_HELI_AILDIRECTION: - g_model.swashR.invertAIL = selectMenuItem(HELI_PARAM_OFS, y, STR_AILDIRECTION, STR_MMMINV, g_model.swashR.invertAIL, 0, 1, attr, event); - break; - - case ITEM_HELI_COLDIRECTION: - g_model.swashR.invertCOL = selectMenuItem(HELI_PARAM_OFS, y, STR_COLDIRECTION, STR_MMMINV, g_model.swashR.invertCOL, 0, 1, attr, event); - break; - } - } -} -#endif - -typedef int16_t (*FnFuncP) (int16_t x); - -int16_t expoFn(int16_t x) -{ - ExpoData *ed = expoAddress(s_currIdx); - int16_t anas[NUM_INPUTS] = {0}; -#if defined(PCBTARANIS) - applyExpos(anas, e_perout_mode_inactive_flight_mode, ed->srcRaw, x); -#else - anas[ed->chn] = x; - applyExpos(anas, e_perout_mode_inactive_flight_mode); -#endif - return anas[ed->chn]; -} - -void DrawFunction(FnFuncP fn, uint8_t offset=0) -{ - lcd_vlineStip(X0-offset, 0, LCD_H, 0xee); - lcd_hlineStip(X0-WCHART-offset, Y0, WCHART*2, 0xee); - - coord_t prev_yv = (coord_t)-1; - - for (int8_t xv=-WCHART; xv<=WCHART; xv++) { - coord_t yv = (LCD_H-1) - (((uint16_t)RESX + fn(xv * (RESX/WCHART))) / 2 * (LCD_H-1) / RESX); - if (prev_yv != (coord_t)-1) { - if (abs((int8_t)yv-prev_yv) <= 1) { - lcd_plot(X0+xv-offset-1, prev_yv, FORCE); - } - else { - uint8_t tmp = (prev_yv < yv ? 0 : 1); - lcd_vline(X0+xv-offset-1, yv+tmp, prev_yv-yv); - } - } - prev_yv = yv; - } -} - - -uint8_t getExpoMixCount(uint8_t expo) -{ - uint8_t count = 0; - uint8_t ch ; - - for(int8_t i=(expo ? MAX_EXPOS-1 : MAX_MIXERS-1); i>=0; i--) { - ch = (expo ? EXPO_VALID(expoAddress(i)) : mixAddress(i)->srcRaw); - if (ch != 0) { - count++; - } - } - return count; -} - -bool reachExpoMixCountLimit(uint8_t expo) -{ - // check mixers count limit - if (getExpoMixCount(expo) >= (expo ? MAX_EXPOS : MAX_MIXERS)) { - POPUP_WARNING(expo ? STR_NOFREEEXPO : STR_NOFREEMIXER); - return true; - } - return false; -} - -void deleteExpoMix(uint8_t expo, uint8_t idx) -{ - pauseMixerCalculations(); - if (expo) { - ExpoData *expo = expoAddress(idx); -#if defined(PCBTARANIS) - int input = expo->chn; -#endif - memmove(expo, expo+1, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); - memclear(&g_model.expoData[MAX_EXPOS-1], sizeof(ExpoData)); -#if defined(PCBTARANIS) - if (!isInputAvailable(input)) { - memclear(&g_model.inputNames[input], LEN_INPUT_NAME); - } -#endif - } - else { - MixData *mix = mixAddress(idx); - memmove(mix, mix+1, (MAX_MIXERS-(idx+1))*sizeof(MixData)); - memclear(&g_model.mixData[MAX_MIXERS-1], sizeof(MixData)); - } - resumeMixerCalculations(); - eeDirty(EE_MODEL); -} - -// TODO avoid this global s_currCh on ARM boards ... -int8_t s_currCh; -void insertExpoMix(uint8_t expo, uint8_t idx) -{ - pauseMixerCalculations(); - if (expo) { - ExpoData *expo = expoAddress(idx); - memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); - memclear(expo, sizeof(ExpoData)); -#if defined(PCBTARANIS) - expo->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channel_order(s_currCh)); - expo->curve.type = CURVE_REF_EXPO; -#endif - expo->mode = 3; // pos&neg - expo->chn = s_currCh - 1; - expo->weight = 100; - } - else { - MixData *mix = mixAddress(idx); - memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData)); - memclear(mix, sizeof(MixData)); - mix->destCh = s_currCh-1; -#if defined(PCBTARANIS) - mix->srcRaw = s_currCh; - if (!isSourceAvailable(mix->srcRaw)) - mix->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channel_order(s_currCh)); -#else - mix->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channel_order(s_currCh)); -#endif - mix->weight = 100; - } - resumeMixerCalculations(); - eeDirty(EE_MODEL); -} - -void copyExpoMix(uint8_t expo, uint8_t idx) -{ - pauseMixerCalculations(); - if (expo) { - ExpoData *expo = expoAddress(idx); - memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); - } - else { - MixData *mix = mixAddress(idx); - memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData)); - } - resumeMixerCalculations(); - eeDirty(EE_MODEL); -} - -void memswap(void *a, void *b, uint8_t size) -{ - uint8_t *x = (uint8_t*)a; - uint8_t *y = (uint8_t*)b; - uint8_t temp ; - - while (size--) { - temp = *x; - *x++ = *y; - *y++ = temp; - } -} - -bool swapExpoMix(uint8_t expo, uint8_t &idx, uint8_t up) -{ - void *x, *y; - uint8_t size; - int8_t tgt_idx = (up ? idx-1 : idx+1); - - if (expo) { - x = (ExpoData *)expoAddress(idx); - - if (tgt_idx < 0) { - if (((ExpoData *)x)->chn == 0) - return false; - ((ExpoData *)x)->chn--; - return true; - } - - if (tgt_idx == MAX_EXPOS) { - if (((ExpoData *)x)->chn == NUM_INPUTS-1) - return false; - ((ExpoData *)x)->chn++; - return true; - } - - y = (ExpoData *)expoAddress(tgt_idx); - if(((ExpoData *)x)->chn != ((ExpoData *)y)->chn || !EXPO_VALID((ExpoData *)y)) { - if (up) { - if (((ExpoData *)x)->chn>0) ((ExpoData *)x)->chn--; - else return false; - } - else { - if (((ExpoData *)x)->chnchn++; - else return false; - } - return true; - } - - size = sizeof(ExpoData); - } - else { - x = (MixData *)mixAddress(idx); - - if (tgt_idx < 0) { - if (((MixData *)x)->destCh == 0) - return false; - ((MixData *)x)->destCh--; - return true; - } - - if (tgt_idx == MAX_MIXERS) { - if (((MixData *)x)->destCh == NUM_CHNOUT-1) - return false; - ((MixData *)x)->destCh++; - return true; - } - - y = (MixData *)mixAddress(tgt_idx); - uint8_t destCh = ((MixData *)x)->destCh; - if(!((MixData *)y)->srcRaw || destCh != ((MixData *)y)->destCh) { - if (up) { - if (destCh>0) ((MixData *)x)->destCh--; - else return false; - } - else { - if (destChdestCh++; - else return false; - } - return true; - } - - size = sizeof(MixData); - } - - pauseMixerCalculations(); - memswap(x, y, size); - resumeMixerCalculations(); - - idx = tgt_idx; - return true; -} - -enum ExposFields { - CASE_PCBTARANIS(EXPO_FIELD_INPUT_NAME) - CASE_CPUARM(EXPO_FIELD_NAME) - CASE_PCBTARANIS(EXPO_FIELD_SOURCE) - CASE_PCBTARANIS(EXPO_FIELD_SCALE) - EXPO_FIELD_WEIGHT, - CASE_PCBTARANIS(EXPO_FIELD_OFFSET) - CASE_9X(EXPO_FIELD_EXPO) - CASE_CURVES(EXPO_FIELD_CURVE) - CASE_FLIGHT_MODES(EXPO_FIELD_FLIGHT_MODES) - EXPO_FIELD_SWITCH, - EXPO_FIELD_SIDE, - CASE_PCBTARANIS(EXPO_FIELD_TRIM) - EXPO_FIELD_MAX -}; - -#if defined(PCBTARANIS) - #define CURVE_ROWS 1 -#else - #define CURVE_ROWS 0 -#endif - -void menuModelExpoOne(uint8_t event) -{ -#if defined(PCBTARANIS) - if (event == EVT_KEY_LONG(KEY_MENU)) { - pushMenu(menuChannelsView); - killEvents(event); - } -#endif - - ExpoData *ed = expoAddress(s_currIdx); -#if defined(PCBTARANIS) - putsMixerSource(7*FW+FW/2, 0, MIXSRC_FIRST_INPUT+ed->chn, 0); -#else - putsMixerSource(7*FW+FW/2, 0, MIXSRC_Rud+ed->chn, 0); -#endif - - SUBMENU(STR_MENUINPUTS, EXPO_FIELD_MAX, {CASE_PCBTARANIS(0) CASE_CPUARM(0) CASE_PCBTARANIS(0) CASE_PCBTARANIS((ed->srcRaw >= MIXSRC_FIRST_TELEM ? (uint8_t)0 : (uint8_t)HIDDEN_ROW)) 0, CASE_PCBTARANIS(0) CASE_9X(0) CASE_CURVES(CURVE_ROWS) CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0 /*, ...*/}); - - SET_SCROLLBAR_X(EXPO_ONE_2ND_COLUMN+10*FW); - - int8_t sub = m_posVert; - - coord_t y = MENU_TITLE_HEIGHT + 1; - -#if defined(PCBTARANIS) - for (unsigned int k=0; k0 ? BLINK|INVERS : INVERS) : 0); - switch(i) - { -#if defined(PCBTARANIS) - case EXPO_FIELD_INPUT_NAME: - editSingleName(EXPO_ONE_2ND_COLUMN, y, STR_INPUTNAME, g_model.inputNames[ed->chn], sizeof(g_model.inputNames[ed->chn]), event, attr); - break; -#endif - -#if defined(CPUARM) - case EXPO_FIELD_NAME: - editSingleName(EXPO_ONE_2ND_COLUMN-IF_9X(sizeof(ed->name)*FW), y, STR_EXPONAME, ed->name, sizeof(ed->name), event, attr); - break; -#endif - -#if defined(PCBTARANIS) - case EXPO_FIELD_SOURCE: - lcd_putsLeft(y, NO_INDENT(STR_SOURCE)); - putsMixerSource(EXPO_ONE_2ND_COLUMN, y, ed->srcRaw, STREXPANDED|attr); - if (attr) ed->srcRaw = checkIncDec(event, ed->srcRaw, INPUTSRC_FIRST, INPUTSRC_LAST, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isInputSourceAvailable); - break; - - case EXPO_FIELD_SCALE: - lcd_putsLeft(y, STR_SCALE); - putsTelemetryChannelValue(EXPO_ONE_2ND_COLUMN, y, ed->srcRaw - MIXSRC_FIRST_TELEM, convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale), LEFT|attr); - if (attr) ed->scale = checkIncDec(event, ed->scale, 0, maxTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1), EE_MODEL); - break; -#endif - - case EXPO_FIELD_WEIGHT: - lcd_putsLeft(y, STR_WEIGHT); - ed->weight = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->weight, MIN_EXPO_WEIGHT, 100, IF_PCBTARANIS(LEFT)|attr, 0, event); - break; - -#if defined(PCBTARANIS) - case EXPO_FIELD_OFFSET: - lcd_putsLeft(y, NO_INDENT(STR_OFFSET)); - ed->offset = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->offset, -100, 100, LEFT|attr, 0, event); - break; -#endif - -#if !defined(PCBTARANIS) - case EXPO_FIELD_EXPO: - lcd_putsLeft(y, STR_EXPO); - if (ed->curveMode==MODE_EXPO || ed->curveParam==0) { - ed->curveMode = MODE_EXPO; - ed->curveParam = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->curveParam, -100, 100, attr, 0, event); - } - else { - lcd_putsAtt(EXPO_ONE_2ND_COLUMN-3*FW, y, STR_NA, attr); - } - break; -#endif - -#if defined(CURVES) - case EXPO_FIELD_CURVE: - lcd_putsLeft(y, STR_CURVE); -#if defined(PCBTARANIS) - editCurveRef(EXPO_ONE_2ND_COLUMN, y, ed->curve, event, attr); -#else - if (ed->curveMode!=MODE_EXPO || ed->curveParam==0) { - putsCurve(EXPO_ONE_2ND_COLUMN-3*FW, y, ed->curveParam, attr); - if (attr) { - CHECK_INCDEC_MODELVAR_ZERO(event, ed->curveParam, CURVE_BASE+MAX_CURVES-1); - if (ed->curveParam) ed->curveMode = MODE_CURVE; - if (ed->curveParam>=CURVE_BASE && event==EVT_KEY_LONG(KEY_ENTER)) { - s_curveChan = ed->curveParam - CURVE_BASE; - pushMenu(menuModelCurveOne); - } - } - } - else { - lcd_putsAtt(EXPO_ONE_2ND_COLUMN-3*FW, y, STR_NA, attr); - } -#endif - break; -#endif - -#if defined(FLIGHT_MODES) - case EXPO_FIELD_FLIGHT_MODES: - ed->flightModes = editFlightModes(EXPO_ONE_2ND_COLUMN-IF_9X(EXPO_ONE_FP_WIDTH), y, event, ed->flightModes, attr); - break; -#endif - - case EXPO_FIELD_SWITCH: - ed->swtch = switchMenuItem(EXPO_ONE_2ND_COLUMN-IF_9X(3*FW), y, ed->swtch, attr, event); - break; - - case EXPO_FIELD_SIDE: - ed->mode = 4 - selectMenuItem(EXPO_ONE_2ND_COLUMN-IF_9X(3*FW), y, STR_SIDE, STR_VSIDE, 4-ed->mode, 1, 3, attr, event); - break; - -#if defined(PCBTARANIS) - case EXPO_FIELD_TRIM: - uint8_t not_stick = (ed->srcRaw > MIXSRC_Ail); - int8_t carryTrim = -ed->carryTrim; - lcd_putsLeft(y, STR_TRIM); - lcd_putsiAtt(EXPO_ONE_2ND_COLUMN, y, STR_VMIXTRIMS, (not_stick && carryTrim == 0) ? 0 : carryTrim+1, m_posHorz==0 ? attr : 0); - if (attr) ed->carryTrim = -checkIncDecModel(event, carryTrim, not_stick ? TRIM_ON : -TRIM_OFF, -TRIM_AIL); - break; -#endif - } - y += FH; - } - - DrawFunction(expoFn); - -#if defined(PCBTARANIS) - int x512 = getValue(ed->srcRaw); - if (ed->srcRaw >= MIXSRC_FIRST_TELEM) { - putsTelemetryChannelValue(LCD_W-8, 6*FH, ed->srcRaw - MIXSRC_FIRST_TELEM, x512, 0); - if (ed->scale > 0) x512 = (x512 * 1024) / convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale); - } - else { - lcd_outdezAtt(LCD_W-8, 6*FH, calcRESXto1000(x512), PREC1); - } - x512 = limit(-1024, x512, 1024); - int y512 = expoFn(x512); - y512 = limit(-1024, y512, 1024); - lcd_outdezAtt(LCD_W-8-6*FW, 1*FH, calcRESXto1000(y512), PREC1); -#else - int16_t x512 = calibratedStick[ed->chn]; - lcd_outdezAtt(LCD_W-8, 6*FH, calcRESXto100(x512), 0); - int16_t y512 = expoFn(x512); - lcd_outdezAtt(LCD_W-8-6*FW, 1*FH, calcRESXto100(y512), 0); -#endif - -#if defined(CPUARM) - x512 = X0+x512/(RESX/WCHART); - y512 = (LCD_H-1) - ((y512+RESX)/2) * (LCD_H-1) / RESX; -#else - x512 = X0+x512/(RESXu/WCHART); - y512 = (LCD_H-1) - (uint16_t)((y512+RESX)/2) * (LCD_H-1) / RESX; -#endif - - lcd_vline(x512, y512-3, 3*2+1); - lcd_hline(x512-3, y512, 3*2+1); -} - -enum MixFields { - CASE_CPUARM(MIX_FIELD_NAME) - MIX_FIELD_SOURCE, - MIX_FIELD_WEIGHT, - MIX_FIELD_OFFSET, - MIX_FIELD_TRIM, - CASE_CURVES(MIX_FIELD_CURVE) - CASE_FLIGHT_MODES(MIX_FIELD_FLIGHT_PHASE) - MIX_FIELD_SWITCH, - MIX_FIELD_WARNING, - MIX_FIELD_MLTPX, - MIX_FIELD_DELAY_UP, - MIX_FIELD_DELAY_DOWN, - MIX_FIELD_SLOW_UP, - MIX_FIELD_SLOW_DOWN, - MIX_FIELD_COUNT -}; - -void gvarWeightItem(coord_t x, coord_t y, MixData *md, uint8_t attr, uint8_t event) -{ - u_int8int16_t weight; - MD_WEIGHT_TO_UNION(md, weight); - weight.word = GVAR_MENU_ITEM(x, y, weight.word, GV_RANGELARGE_WEIGHT_NEG, GV_RANGELARGE_WEIGHT, attr, 0, event); - MD_UNION_TO_WEIGHT(weight, md); -} - -#if !defined(CPUM64) || !defined(FRSKY) -#define GAUGE_WIDTH 33 -#define GAUGE_HEIGHT 6 -void drawOffsetBar(uint8_t x, uint8_t y, MixData * md) -{ - int offset = GET_GVAR(MD_OFFSET(md), GV_RANGELARGE_NEG, GV_RANGELARGE, mixerCurrentFlightMode); - int weight = abs(GET_GVAR(MD_WEIGHT(md), GV_RANGELARGE_NEG, GV_RANGELARGE, mixerCurrentFlightMode)); - int barMin = offset - weight; - int barMax = offset + weight; - if (y > 15) { -#if defined(CPUARM) - lcd_outdezAtt(x-((barMin >= 0) ? 2 : 3), y-6, barMin, TINSIZE|LEFT); - lcd_outdezAtt(x+GAUGE_WIDTH+1, y-6, barMax, TINSIZE); -#else - lcd_outdezAtt(x-((barMin >= 0) ? 2 : 3), y-8, barMin, LEFT); - lcd_outdezAtt(x+GAUGE_WIDTH+1, y-8, barMax); -#endif - } - if (barMin < -101) - barMin = -101; - if (barMax > 101) - barMax = 101; - lcd_hlineStip(x-2, y, GAUGE_WIDTH+2, DOTTED); - lcd_hlineStip(x-2, y+GAUGE_HEIGHT, GAUGE_WIDTH+2, DOTTED); - lcd_vline(x-2, y+1, GAUGE_HEIGHT-1); - lcd_vline(x+GAUGE_WIDTH-1, y+1, GAUGE_HEIGHT-1); - if (barMin <= barMax) { - int8_t right = (barMax * GAUGE_WIDTH) / 200; - int8_t left = ((barMin * GAUGE_WIDTH) / 200)-1; - lcd_filled_rect(x+GAUGE_WIDTH/2+left, y+2, right-left, GAUGE_HEIGHT-3); - } - lcd_vline(x+GAUGE_WIDTH/2-1, y, GAUGE_HEIGHT+1); - if (barMin == -101) { - for (uint8_t i=0; i<3; ++i) { - lcd_plot(x+i, y+4-i); - lcd_plot(x+3+i, y+4-i); - } - } - if (barMax == 101) { - for (uint8_t i=0; i<3; ++i) { - lcd_plot(x+GAUGE_WIDTH-8+i, y+4-i); - lcd_plot(x+GAUGE_WIDTH-5+i, y+4-i); - } - } -} -#undef GAUGE_WIDTH -#undef GAUGE_HEIGHT -#endif - -void menuModelMixOne(uint8_t event) -{ -#if defined(PCBTARANIS) - if (event == EVT_KEY_LONG(KEY_MENU)) { - pushMenu(menuChannelsView); - killEvents(event); - } -#endif - - TITLE(s_currCh ? STR_INSERTMIX : STR_EDITMIX); - MixData *md2 = mixAddress(s_currIdx) ; - putsChn(lcdLastPos+1*FW, 0, md2->destCh+1,0); - -#if defined(ROTARY_ENCODERS) -#if defined(CURVES) - if ((m_posVert == MIX_FIELD_TRIM && md2->srcRaw > NUM_STICKS) || (m_posVert == MIX_FIELD_CURVE && md2->curveMode == MODE_CURVE)) -#else - if (m_posVert == MIX_FIELD_TRIM && md2->srcRaw > NUM_STICKS) -#endif - SUBMENU_NOTITLE(MIX_FIELD_COUNT, {CASE_CPUARM(0) 0, 0, 0, CASE_9X(0) CASE_CURVES(0) CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0, 0 /*, ...*/}) - else - SUBMENU_NOTITLE(MIX_FIELD_COUNT, {CASE_CPUARM(0) 0, 0, 0, CASE_9X(1) CASE_CURVES(1) CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0, 0 /*, ...*/}); -#else - SUBMENU_NOTITLE(MIX_FIELD_COUNT, {CASE_CPUARM(0) 0, 0, 0, CASE_9X(1) CASE_PCBTARANIS(0) CASE_CURVES(1) CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0, 0 /*, ...*/}); -#endif - -#if MENU_COLUMNS > 1 - lcd_vline(MENU_COLUMN2_X-4, FH+1, LCD_H-FH-1); -#endif - - int8_t sub = m_posVert; - int8_t editMode = s_editMode; - - for (uint8_t k=0; k 1 - coord_t y; - coord_t COLUMN_X; - if (k >= LCD_LINES-1) { - y = 1 + (k-LCD_LINES+2)*FH; - COLUMN_X = MENU_COLUMN2_X; - } - else { - y = 1 + (k+1)*FH; - COLUMN_X = 0; - } - int8_t i = k; -#else - coord_t y = MENU_TITLE_HEIGHT + 1 + k*FH; - int8_t i = k + s_pgOfs; -#endif - - uint8_t attr = (sub==i ? (editMode>0 ? BLINK|INVERS : INVERS) : 0); - switch(i) { -#if defined(CPUARM) - case MIX_FIELD_NAME: - editSingleName(COLUMN_X+MIXES_2ND_COLUMN, y, STR_MIXNAME, md2->name, sizeof(md2->name), event, attr); - break; -#endif - case MIX_FIELD_SOURCE: - lcd_putsColumnLeft(COLUMN_X, y, NO_INDENT(STR_SOURCE)); - putsMixerSource(COLUMN_X+MIXES_2ND_COLUMN, y, md2->srcRaw, STREXPANDED|attr); - if (attr) CHECK_INCDEC_MODELSOURCE(event, md2->srcRaw, 1, MIXSRC_LAST); - break; - case MIX_FIELD_WEIGHT: - lcd_putsColumnLeft(COLUMN_X, y, STR_WEIGHT); - gvarWeightItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2, attr|LEFT, event); - break; - case MIX_FIELD_OFFSET: - { - lcd_putsColumnLeft(COLUMN_X, y, NO_INDENT(STR_OFFSET)); - u_int8int16_t offset; - MD_OFFSET_TO_UNION(md2, offset); - offset.word = GVAR_MENU_ITEM(COLUMN_X+MIXES_2ND_COLUMN, y, offset.word, GV_RANGELARGE_OFFSET_NEG, GV_RANGELARGE_OFFSET, attr|LEFT, 0, event); - MD_UNION_TO_OFFSET(offset, md2); -#if !defined(CPUM64) || !defined(FRSKY) - drawOffsetBar(COLUMN_X+MIXES_2ND_COLUMN+22, y, md2); -#endif - break; - } - -#if defined(PCBTARANIS) - case MIX_FIELD_TRIM: - lcd_putsColumnLeft(COLUMN_X, y, STR_TRIM); - menu_lcd_onoff(COLUMN_X+MIXES_2ND_COLUMN, y, !md2->carryTrim, attr); - if (attr) md2->carryTrim = !checkIncDecModel(event, !md2->carryTrim, 0, 1); - break; -#else - case MIX_FIELD_TRIM: - { - uint8_t not_stick = (md2->srcRaw > NUM_STICKS); - int8_t carryTrim = -md2->carryTrim; - lcd_putsColumnLeft(COLUMN_X, y, STR_TRIM); - lcd_putsiAtt((not_stick ? COLUMN_X+MIXES_2ND_COLUMN : COLUMN_X+6*FW-3), y, STR_VMIXTRIMS, (not_stick && carryTrim == 0) ? 0 : carryTrim+1, m_posHorz==0 ? attr : 0); - if (attr && m_posHorz==0 && (not_stick || editMode>0)) md2->carryTrim = -checkIncDecModel(event, carryTrim, not_stick ? TRIM_ON : -TRIM_OFF, -TRIM_AIL); - if (!not_stick) { - lcd_puts(COLUMN_X+MIXES_2ND_COLUMN, y, STR_DREX); - menu_lcd_onoff(COLUMN_X+MIXES_2ND_COLUMN+DREX_CHBOX_OFFSET, y, !md2->noExpo, m_posHorz==1 ? attr : 0); - if (attr && m_posHorz==1 && editMode>0) md2->noExpo = !checkIncDecModel(event, !md2->noExpo, 0, 1); - } - else if (attr) { - REPEAT_LAST_CURSOR_MOVE(); - } - break; - } -#endif - -#if defined(CURVES) - case MIX_FIELD_CURVE: - { - lcd_putsColumnLeft(COLUMN_X, y, STR_CURVE); -#if defined(PCBTARANIS) - editCurveRef(COLUMN_X+MIXES_2ND_COLUMN, y, md2->curve, event, attr); -#else - int8_t curveParam = md2->curveParam; - if (md2->curveMode == MODE_CURVE) { - putsCurve(COLUMN_X+MIXES_2ND_COLUMN, y, curveParam, attr); - if (attr) { - if (event==EVT_KEY_LONG(KEY_ENTER) && (curveParam<0 || curveParam>=CURVE_BASE)){ - s_curveChan = (curveParam<0 ? -curveParam-1 : curveParam-CURVE_BASE); - pushMenu(menuModelCurveOne); - } - else { - CHECK_INCDEC_MODELVAR(event, md2->curveParam, -MAX_CURVES, CURVE_BASE+MAX_CURVES-1); - if (md2->curveParam == 0) - md2->curveMode = MODE_DIFFERENTIAL; -#if defined(ROTARY_ENCODER_NAVIGATION) || defined(PCBTARANIS) - MOVE_CURSOR_FROM_HERE(); -#else - m_posHorz = 0; -#endif - } - } - } - else { - lcd_putsAtt(COLUMN_X+MIXES_2ND_COLUMN, y, PSTR("Diff"), m_posHorz==0 ? attr : 0); - md2->curveParam = GVAR_MENU_ITEM(COLUMN_X+MIXES_2ND_COLUMN+5*FW, y, curveParam, -100, 100, LEFT|(m_posHorz==1 ? attr : 0), 0, editMode>0 ? event : 0); - if (attr && editMode>0 && m_posHorz==0) { - int8_t tmp = 0; - CHECK_INCDEC_MODELVAR(event, tmp, -1, 1); - if (tmp != 0) { - md2->curveMode = MODE_CURVE; - md2->curveParam = tmp; - } - } - } -#endif - break; - } -#endif -#if defined(FLIGHT_MODES) - case MIX_FIELD_FLIGHT_PHASE: - md2->flightModes = editFlightModes(COLUMN_X+MIXES_2ND_COLUMN, y, event, md2->flightModes, attr); - break; -#endif - case MIX_FIELD_SWITCH: - md2->swtch = switchMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2->swtch, attr, event); - break; - case MIX_FIELD_WARNING: - lcd_putsColumnLeft(COLUMN_X+MIXES_2ND_COLUMN, y, STR_MIXWARNING); - if (md2->mixWarn) - lcd_outdezAtt(COLUMN_X+MIXES_2ND_COLUMN, y, md2->mixWarn, attr|LEFT); - else - lcd_putsAtt(COLUMN_X+MIXES_2ND_COLUMN, y, STR_OFF, attr); - if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, md2->mixWarn, 3); - break; - case MIX_FIELD_MLTPX: - md2->mltpx = selectMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, STR_MULTPX, STR_VMLTPX, md2->mltpx, 0, 2, attr, event); - break; - case MIX_FIELD_DELAY_UP: - md2->delayUp = EDIT_DELAY(COLUMN_X, y, event, attr, STR_DELAYUP, md2->delayUp); - break; - case MIX_FIELD_DELAY_DOWN: - md2->delayDown = EDIT_DELAY(COLUMN_X, y, event, attr, STR_DELAYDOWN, md2->delayDown); - break; - case MIX_FIELD_SLOW_UP: - md2->speedUp = EDIT_DELAY(COLUMN_X, y, event, attr, STR_SLOWUP, md2->speedUp); - break; - case MIX_FIELD_SLOW_DOWN: - md2->speedDown = EDIT_DELAY(COLUMN_X, y, event, attr, STR_SLOWDOWN, md2->speedDown); - break; - } - } -} - -static uint8_t s_maxLines = 8; -static uint8_t s_copySrcIdx; -static uint8_t s_copySrcCh; - -#define _STR_MAX(x) PSTR("/" #x) -#define STR_MAX(x) _STR_MAX(x) - -#if LCD_W >= 212 - #define EXPO_LINE_WEIGHT_POS 8*FW+8 - #define EXPO_LINE_SRC_POS 9*FW+3 - #define EXPO_LINE_CURVE_POS 12*FW+11 - #define EXPO_LINE_TRIM_POS 19*FW-4 - #define EXPO_LINE_SWITCH_POS 20*FW-1 - #define EXPO_LINE_SIDE_POS 25*FW - #define EXPO_LINE_FM_POS 12*FW+11 - #define EXPO_LINE_SELECT_POS 5*FW+2 - #define EXPO_LINE_NAME_POS LCD_W-LEN_EXPOMIX_NAME*FW-MENUS_SCROLLBAR_WIDTH - #define MIX_LINE_WEIGHT_POS 6*FW+8 - #define MIX_LINE_SRC_POS 7*FW+3 - #define MIX_LINE_CURVE_POS 13*FW+3 - #define MIX_LINE_SWITCH_POS 19*FW+1 - #define MIX_LINE_FM_POS 13*FW+3 - #define MIX_LINE_DELAY_POS 24*FW+3 -#elif defined(CPUARM) - #define EXPO_LINE_WEIGHT_POS 7*FW+1 - #define EXPO_LINE_EXPO_POS 10*FW+5 - #define EXPO_LINE_SWITCH_POS 11*FW+2 - #define EXPO_LINE_SIDE_POS 14*FW+2 - #define EXPO_LINE_SELECT_POS 24 - #define EXPO_LINE_FM_POS - #define EXPO_LINE_NAME_POS LCD_W-LEN_EXPOMIX_NAME*FW-MENUS_SCROLLBAR_WIDTH - #define MIX_LINE_SRC_POS 4*FW-1 - #define MIX_LINE_WEIGHT_POS 11*FW+3 - #define MIX_LINE_CURVE_POS 12*FW+2 - #define MIX_LINE_SWITCH_POS 16*FW - #define MIX_LINE_DELAY_POS 19*FW+7 -#else - #define EXPO_LINE_WEIGHT_POS 7*FW+1 - #define EXPO_LINE_EXPO_POS 11*FW - #define EXPO_LINE_SWITCH_POS 11*FW+4 - #if MAX_FLIGHT_MODES == 6 - #define EXPO_LINE_SIDE_POS 15*FW - #else - #define EXPO_LINE_SIDE_POS 15*FW+2 - #endif - #define EXPO_LINE_FM_POS LCD_W-FW-MENUS_SCROLLBAR_WIDTH - #define EXPO_LINE_SELECT_POS 24 - #define MIX_LINE_SRC_POS 4*FW-1 - #define MIX_LINE_WEIGHT_POS 11*FW+3 - #define MIX_LINE_CURVE_POS 12*FW+2 - #define MIX_LINE_SWITCH_POS 16*FW - #define MIX_LINE_DELAY_POS 19*FW+7 -#endif - -#if defined(NAVIGATION_MENUS) -void onExpoMixMenu(const char *result) -{ - bool expo = (g_menuStack[g_menuStackPtr] == menuModelExposAll); - uint8_t chn = (expo ? expoAddress(s_currIdx)->chn+1 : mixAddress(s_currIdx)->destCh+1); - - if (result == STR_EDIT) { - pushMenu(expo ? menuModelExpoOne : menuModelMixOne); - } - else if (result == STR_INSERT_BEFORE || result == STR_INSERT_AFTER) { - if (!reachExpoMixCountLimit(expo)) { - s_currCh = chn; - if (result == STR_INSERT_AFTER) { s_currIdx++; m_posVert++; } - insertExpoMix(expo, s_currIdx); - pushMenu(expo ? menuModelExpoOne : menuModelMixOne); - } - } - else if (result == STR_COPY || result == STR_MOVE) { - s_copyMode = (result == STR_COPY ? COPY_MODE : MOVE_MODE); - s_copySrcIdx = s_currIdx; - s_copySrcCh = chn; - s_copySrcRow = m_posVert; - } - else if (result == STR_DELETE) { - deleteExpoMix(expo, s_currIdx); - } -} -#endif - -#if LCD_W >= 212 -void displayHeaderChannelName(uint8_t ch) -{ - uint8_t len = zlen(g_model.limitData[ch-1].name, sizeof(g_model.limitData[ch-1].name)); - if (len) { - lcd_putc(17*FW, 0, ' '); - lcd_putsnAtt(lcdNextPos, 0, g_model.limitData[ch-1].name, len, ZCHAR); - lcd_putc(lcdNextPos, 0, ' '); - } -} -#endif - -void displayMixInfos(coord_t y, MixData *md) -{ -#if defined(PCBTARANIS) - putsCurveRef(MIX_LINE_CURVE_POS, y, md->curve, 0); -#else - if (md->curveParam) { - if (md->curveMode == MODE_CURVE) - putsCurve(MIX_LINE_CURVE_POS, y, md->curveParam); - else - displayGVar(MIX_LINE_CURVE_POS+3*FW, y, md->curveParam, -100, 100); - } -#endif - - if (md->swtch) { - putsSwitches(MIX_LINE_SWITCH_POS, y, md->swtch); - } -} - -#if defined(PCBTARANIS) -void displayMixLine(coord_t y, MixData *md) -{ - if (md->name[0]) { - lcd_putsnAtt(EXPO_LINE_NAME_POS, y, md->name, sizeof(md->name), ZCHAR); - } - - if (!md->flightModes || ((md->curve.value || md->swtch) && ((get_tmr10ms() / 200) & 1))) - displayMixInfos(y, md); - else - displayFlightModes(MIX_LINE_FM_POS, y, md->flightModes); -} -#elif defined(CPUARM) -void displayMixLine(coord_t y, MixData *md) -{ - if (md->name[0]) { - lcd_putsnAtt(EXPO_LINE_NAME_POS, y, md->name, sizeof(md->name), ZCHAR); - } - else { - displayMixInfos(y, md); - } -} -#else -#define displayMixLine(y, md) displayMixInfos(y, md) -#endif - -void displayExpoInfos(coord_t y, ExpoData *ed) -{ -#if defined(PCBTARANIS) - putsCurveRef(EXPO_LINE_CURVE_POS, y, ed->curve, 0); -#else - if (ed->curveMode == MODE_CURVE) - putsCurve(EXPO_LINE_EXPO_POS-3*FW, y, ed->curveParam); - else - displayGVar(EXPO_LINE_EXPO_POS, y, ed->curveParam, -100, 100); -#endif - - putsSwitches(EXPO_LINE_SWITCH_POS, y, ed->swtch, 0); -} - -#if defined(PCBHORUS) -void displayExpoLine(coord_t y, ExpoData *ed) -{ - putsMixerSource(EXPO_LINE_SRC_POS, y, ed->srcRaw, 0); - - if (ed->carryTrim != TRIM_ON) { - lcd_putc(EXPO_LINE_TRIM_POS, y, ed->carryTrim > 0 ? '-' : STR_RETA123[-ed->carryTrim]); - } - - if (!ed->flightModes || ((ed->curve.value || ed->swtch) && ((get_tmr10ms() / 200) & 1))) - displayExpoInfos(y, ed); - else - displayFlightModes(EXPO_LINE_FM_POS, y, ed->flightModes); - - if (ed->name[0]) { - lcd_putsnAtt(EXPO_LINE_NAME_POS, y, ed->name, sizeof(ed->name), ZCHAR); - } -} -#elif defined(PCBTARANIS) -void displayExpoLine(coord_t y, ExpoData *ed) -{ - putsMixerSource(EXPO_LINE_SRC_POS, y, ed->srcRaw, 0); - - if (ed->carryTrim != TRIM_ON) { - lcd_putc(EXPO_LINE_TRIM_POS, y, ed->carryTrim > 0 ? '-' : STR_RETA123[-ed->carryTrim]); - } - - if (!ed->flightModes || ((ed->curve.value || ed->swtch) && ((get_tmr10ms() / 200) & 1))) - displayExpoInfos(y, ed); - else - displayFlightModes(EXPO_LINE_FM_POS, y, ed->flightModes); - - if (ed->name[0]) { - lcd_putsnAtt(EXPO_LINE_NAME_POS, y, ed->name, sizeof(ed->name), ZCHAR); - } -} -#elif defined(CPUARM) -void displayExpoLine(coord_t y, ExpoData *ed) -{ - displayExpoInfos(y, ed); - - if (ed->name[0]) { - lcd_putsnAtt(EXPO_LINE_NAME_POS, y, ed->name, sizeof(ed->name), ZCHAR); - } -} -#else -#define displayExpoLine(y, ed) \ - displayExpoInfos(y, ed); \ - displayFlightModes(EXPO_LINE_FM_POS, y, ed->flightModes) -#endif - -void menuModelExpoMix(uint8_t expo, uint8_t event) -{ - uint8_t sub = m_posVert; - - if (s_editMode > 0) - s_editMode = 0; - - uint8_t chn = (expo ? expoAddress(s_currIdx)->chn+1 : mixAddress(s_currIdx)->destCh+1); - - switch (event) - { - case EVT_ENTRY: - case EVT_ENTRY_UP: - s_copyMode = 0; - s_copyTgtOfs = 0; - break; - case EVT_KEY_LONG(KEY_EXIT): - if (s_copyMode && s_copyTgtOfs == 0) { - deleteExpoMix(expo, s_currIdx); - killEvents(event); - event = 0; - } - // no break -#if defined(ROTARY_ENCODER_NAVIGATION) - case EVT_ROTARY_LONG: - if (s_copyMode) { - killEvents(event); - } -#endif - case EVT_KEY_BREAK(KEY_EXIT): - if (s_copyMode) { - if (s_copyTgtOfs) { - // cancel the current copy / move operation - if (s_copyMode == COPY_MODE) { - deleteExpoMix(expo, s_currIdx); - } - else { - do { - swapExpoMix(expo, s_currIdx, s_copyTgtOfs > 0); - s_copyTgtOfs += (s_copyTgtOfs < 0 ? +1 : -1); - } while (s_copyTgtOfs != 0); - eeDirty(EE_MODEL); - } - m_posVert = s_copySrcRow; - s_copyTgtOfs = 0; - } - s_copyMode = 0; - event = 0; - } - break; - case EVT_KEY_BREAK(KEY_ENTER): - if (sub != 0 && (!s_currCh || (s_copyMode && !s_copyTgtOfs)) && !READ_ONLY()) { - s_copyMode = (s_copyMode == COPY_MODE ? MOVE_MODE : COPY_MODE); - s_copySrcIdx = s_currIdx; - s_copySrcCh = chn; - s_copySrcRow = sub; - break; - } - // no break - - CASE_EVT_ROTARY_BREAK - case EVT_KEY_LONG(KEY_ENTER): - killEvents(event); - if (s_copyTgtOfs) { - s_copyMode = 0; - s_copyTgtOfs = 0; - } - else if (sub != 0) { - if (READ_ONLY()) { - if (!s_currCh) { - pushMenu(expo ? menuModelExpoOne : menuModelMixOne); - } - } - else { - if (s_copyMode) s_currCh = 0; -#if defined(NAVIGATION_MENUS) - if (s_currCh) { - if (reachExpoMixCountLimit(expo)) break; - insertExpoMix(expo, s_currIdx); - pushMenu(expo ? menuModelExpoOne : menuModelMixOne); - s_copyMode = 0; - } - else { - event = 0; - s_copyMode = 0; - MENU_ADD_ITEM(STR_EDIT); - MENU_ADD_ITEM(STR_INSERT_BEFORE); - MENU_ADD_ITEM(STR_INSERT_AFTER); - MENU_ADD_ITEM(STR_COPY); - MENU_ADD_ITEM(STR_MOVE); - MENU_ADD_ITEM(STR_DELETE); - menuHandler = onExpoMixMenu; - } -#else - if (s_currCh) { - if (reachExpoMixCountLimit(expo)) break; - insertExpoMix(expo, s_currIdx); - } - pushMenu(expo ? menuModelExpoOne : menuModelMixOne); - s_copyMode = 0; -#endif - } - } - break; - case EVT_KEY_LONG(KEY_LEFT): - case EVT_KEY_LONG(KEY_RIGHT): - if (s_copyMode && !s_copyTgtOfs) { - if (reachExpoMixCountLimit(expo)) break; - s_currCh = chn; - if (event == EVT_KEY_LONG(KEY_RIGHT)) { s_currIdx++; m_posVert++; } - insertExpoMix(expo, s_currIdx); - pushMenu(expo ? menuModelExpoOne : menuModelMixOne); - s_copyMode = 0; - killEvents(event); - } - break; -#if defined(ROTARY_ENCODER_NAVIGATION) - case EVT_ROTARY_LEFT: - case EVT_ROTARY_RIGHT: -#endif - case EVT_KEY_FIRST(KEY_MOVE_UP): - case EVT_KEY_REPT(KEY_MOVE_UP): - case EVT_KEY_FIRST(KEY_MOVE_DOWN): - case EVT_KEY_REPT(KEY_MOVE_DOWN): - if (s_copyMode) { - uint8_t key = (event & 0x1f); - uint8_t next_ofs = ((IS_ROTARY_UP(event) || key==KEY_MOVE_UP) ? s_copyTgtOfs - 1 : s_copyTgtOfs + 1); - - if (s_copyTgtOfs==0 && s_copyMode==COPY_MODE) { - // insert a mix on the same channel (just above / just below) - if (reachExpoMixCountLimit(expo)) break; - copyExpoMix(expo, s_currIdx); - if (IS_ROTARY_DOWN(event) || key==KEY_MOVE_DOWN) s_currIdx++; - else if (sub-s_pgOfs >= 6) s_pgOfs++; - } - else if (next_ofs==0 && s_copyMode==COPY_MODE) { - // delete the mix - deleteExpoMix(expo, s_currIdx); - if (IS_ROTARY_UP(event) || key==KEY_MOVE_UP) s_currIdx--; - } - else { - // only swap the mix with its neighbor - if (!swapExpoMix(expo, s_currIdx, IS_ROTARY_UP(event) || key==KEY_MOVE_UP)) break; - eeDirty(EE_MODEL); - } - - s_copyTgtOfs = next_ofs; - } - break; - } - -#if !defined(PCBHORUS) - lcd_outdezAtt(FW*max(sizeof(TR_MENUINPUTS), sizeof(TR_MIXER))+FW+FW/2, 0, getExpoMixCount(expo)); - lcd_puts(FW*max(sizeof(TR_MENUINPUTS), sizeof(TR_MIXER))+FW+FW/2, 0, expo ? STR_MAX(MAX_EXPOS) : STR_MAX(MAX_MIXERS)); -#endif - - SIMPLE_MENU(expo ? STR_MENUINPUTS : STR_MIXER, menuTabModel, expo ? e_InputsAll : e_MixAll, s_maxLines); - -#if defined(PCBHORUS) - lcd_outdezAtt(15+10*max(sizeof(TR_MENUINPUTS), sizeof(TR_MIXER)), 6, getExpoMixCount(expo), MIDSIZE|WHITE); - lcd_putsAtt(15+10*max(sizeof(TR_MENUINPUTS), sizeof(TR_MIXER)), 6, expo ? STR_MAX(MAX_EXPOS) : STR_MAX(MAX_MIXERS), MIDSIZE|WHITE); -#endif - - sub = m_posVert; - s_currCh = 0; - uint8_t cur = 1; - uint8_t i = 0; - - for (uint8_t ch=1; ch<=(expo ? NUM_INPUTS : NUM_CHNOUT); ch++) { - void *pointer = NULL; MixData * &md = (MixData * &)pointer; ExpoData * &ed = (ExpoData * &)pointer; - coord_t y = MENU_TITLE_HEIGHT-FH+1+(cur-s_pgOfs)*FH; - if (expo ? (ichn+1 == ch && EXPO_VALID(ed)) : (isrcRaw && md->destCh+1 == ch)) { - if (s_pgOfs < cur && cur-s_pgOfs < LCD_LINES) { - if (expo) { -#if defined(PCBTARANIS) - putsMixerSource(0, y, ch, 0); -#else - putsMixerSource(0, y, MIXSRC_Rud+ch-1, 0); -#endif - } - else { - putsChn(0, y, ch, 0); // show CHx - } - } - uint8_t mixCnt = 0; - do { - if (s_copyMode) { - if (s_copyMode == MOVE_MODE && s_pgOfs < cur && cur-s_pgOfs < 8 && s_copySrcCh == ch && s_copyTgtOfs != 0 && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { - lcd_rect(expo ? 18 : 22, y-1, expo ? LCD_W-18 : LCD_W-22, 9, DOTTED); - cur++; y+=FH; - } - if (s_currIdx == i) { - sub = m_posVert = cur; - s_currCh = ch; - } - } - else if (sub == cur) { - s_currIdx = i; - } - if (s_pgOfs < cur && cur-s_pgOfs < 8) { - uint8_t attr = ((s_copyMode || sub != cur) ? 0 : INVERS); - if (expo) { - ed->weight = GVAR_MENU_ITEM(EXPO_LINE_WEIGHT_POS, y, ed->weight, MIN_EXPO_WEIGHT, 100, attr | (isExpoActive(i) ? BOLD : 0), 0, event); - displayExpoLine(y, ed); - if (ed->mode!=3) { - lcd_putc(EXPO_LINE_SIDE_POS, y, ed->mode == 2 ? 126 : 127); - } - } - else { -#if LCD_W >= 212 - if (attr) { - displayHeaderChannelName(ch); - } -#endif - - if (mixCnt > 0) lcd_putsiAtt(FW, y, STR_VMLTPX2, md->mltpx, 0); - - putsMixerSource(MIX_LINE_SRC_POS, y, md->srcRaw, 0); - - gvarWeightItem(MIX_LINE_WEIGHT_POS, y, md, attr | (isMixActive(i) ? BOLD : 0), event); - - displayMixLine(y, md); - - char cs = ' '; - if (md->speedDown || md->speedUp) - cs = 'S'; - if (md->delayUp || md->delayDown) - cs = (cs =='S' ? '*' : 'D'); - lcd_putc(MIX_LINE_DELAY_POS, y, cs); - } - if (s_copyMode) { - if ((s_copyMode==COPY_MODE || s_copyTgtOfs == 0) && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { - /* draw a border around the raw on selection mode (copy/move) */ - lcd_rect(expo ? EXPO_LINE_SELECT_POS : 22, y-1, expo ? (LCD_W-EXPO_LINE_SELECT_POS) : (LCD_W-22), 9, s_copyMode == COPY_MODE ? SOLID : DOTTED); - } - if (cur == sub) { - /* invert the raw when it's the current one */ - lcd_filled_rect(expo ? EXPO_LINE_SELECT_POS+1 : 23, y, expo ? (LCD_W-EXPO_LINE_SELECT_POS-2) : (LCD_W-24), 7); - } - } - } - cur++; y+=FH; mixCnt++; i++; if (expo) ed++; else md++; - } while (expo ? (ichn+1 == ch && EXPO_VALID(ed)) : (isrcRaw && md->destCh+1 == ch)); - if (s_copyMode == MOVE_MODE && s_pgOfs < cur && cur-s_pgOfs < LCD_LINES && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { - lcd_rect(expo ? EXPO_LINE_SELECT_POS : 22, y-1, expo ? LCD_W-EXPO_LINE_SELECT_POS : LCD_W-22, 9, DOTTED); - cur++; y+=FH; - } - } - else { - uint8_t attr = 0; - if (sub == cur) { - s_currIdx = i; - s_currCh = ch; - if (!s_copyMode) { - attr = INVERS; - } - } - if (s_pgOfs < cur && cur-s_pgOfs < LCD_LINES) { - if (expo) { -#if defined(PCBTARANIS) - putsMixerSource(0, y, ch, attr); -#else - putsMixerSource(0, y, MIXSRC_Rud+ch-1, attr); -#endif - } - else { - putsChn(0, y, ch, attr); // show CHx -#if LCD_W >= 212 - if (attr) { - displayHeaderChannelName(ch); - } -#endif - } - if (s_copyMode == MOVE_MODE && s_copySrcCh == ch) { - lcd_rect(expo ? EXPO_LINE_SELECT_POS : 22, y-1, expo ? (LCD_W-EXPO_LINE_SELECT_POS) : (LCD_W-22), 9, DOTTED); - } - } - cur++; y+=FH; - } - } - s_maxLines = cur; - if (sub >= s_maxLines-1) m_posVert = s_maxLines-1; -} - -void menuModelExposAll(uint8_t event) -{ - return menuModelExpoMix(1, event); -} - -void menuModelMixAll(uint8_t event) -{ - return menuModelExpoMix(0, event); -} - #if defined(TEMPLATES) void menuModelTemplates(uint8_t event) { diff --git a/radio/src/gui/menu_model_flightmodes.cpp b/radio/src/gui/menu_model_flightmodes.cpp new file mode 100755 index 000000000..69d9e7b87 --- /dev/null +++ b/radio/src/gui/menu_model_flightmodes.cpp @@ -0,0 +1,411 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "../opentx.h" + +#if defined(PCBTARANIS) +void displayFlightModes(coord_t x, coord_t y, FlightModesType value) +{ + lcd_puts(x, y, STR_FP); + x = lcdNextPos + 1; + for (uint8_t p=0; p 0) { posHorz += 1; } + + if (sub=0) { + displayColumnHeader(STR_PHASES_HEADERS, posHorz); + } + + for (uint8_t i=0; i0) ? BLINK|INVERS : INVERS) : 0); + uint8_t active = (attr && (s_editMode>0 || p1valdiff)) ; + switch (j) { + case ITEM_FLIGHT_MODES_NAME: + editName(4*FW-1, y, p->name, sizeof(p->name), event, attr); + break; + + case ITEM_FLIGHT_MODES_SWITCH: + putsSwitches((5+LEN_FLIGHT_MODE_NAME)*FW+FW/2, y, p->swtch, attr); + if (active) CHECK_INCDEC_MODELSWITCH(event, p->swtch, SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, isSwitchAvailableInMixes); + break; + + case ITEM_FLIGHT_MODES_TRIM_RUD: + case ITEM_FLIGHT_MODES_TRIM_ELE: + case ITEM_FLIGHT_MODES_TRIM_THR: + case ITEM_FLIGHT_MODES_TRIM_AIL: + { + uint8_t t = j-ITEM_FLIGHT_MODES_TRIM_RUD; + putsTrimMode((4+LEN_FLIGHT_MODE_NAME)*FW+j*(5*FW/2), y, k, t, attr); + if (active) { + trim_t & v = p->trim[t]; + v.mode = checkIncDec(event, v.mode==TRIM_MODE_NONE ? -1 : v.mode, -1, k==0 ? 0 : 2*MAX_FLIGHT_MODES-1, EE_MODEL, isTrimModeAvailable); + } + break; + } + + case ITEM_FLIGHT_MODES_FADE_IN: + lcd_outdezAtt(32*FW-2, y, (10/DELAY_STEP)*p->fadeIn, attr|PREC1); + if (active) p->fadeIn = checkIncDec(event, p->fadeIn, 0, DELAY_MAX, EE_MODEL|NO_INCDEC_MARKS); + break; + + case ITEM_FLIGHT_MODES_FADE_OUT: + lcd_outdezAtt(35*FW, y, (10/DELAY_STEP)*p->fadeOut, attr|PREC1); + if (active) p->fadeOut = checkIncDec(event, p->fadeOut, 0, DELAY_MAX, EE_MODEL|NO_INCDEC_MARKS); + break; + + } + } + } +} + +#else // PCBTARANIS + +enum menuModelPhaseItems { + ITEM_MODEL_PHASE_NAME, + ITEM_MODEL_PHASE_SWITCH, + ITEM_MODEL_PHASE_TRIMS, + IF_ROTARY_ENCODERS(ITEM_MODEL_PHASE_ROTARY_ENCODERS) + ITEM_MODEL_PHASE_FADE_IN, + ITEM_MODEL_PHASE_FADE_OUT, +#if defined(GVARS) && !defined(PCBSTD) + ITEM_MODEL_PHASE_GVARS_LABEL, + ITEM_MODEL_PHASE_GV1, + ITEM_MODEL_PHASE_GV2, + ITEM_MODEL_PHASE_GV3, + ITEM_MODEL_PHASE_GV4, + ITEM_MODEL_PHASE_GV5, +#if defined(CPUARM) + ITEM_MODEL_PHASE_GV6, + ITEM_MODEL_PHASE_GV7, + ITEM_MODEL_PHASE_GV8, + ITEM_MODEL_PHASE_GV9, +#endif +#endif + ITEM_MODEL_PHASE_MAX +}; + +void menuModelPhaseOne(uint8_t event) +{ + FlightModeData *fm = flightModeAddress(s_currIdx); + putsFlightMode(13*FW, 0, s_currIdx+1, (getFlightMode()==s_currIdx ? BOLD : 0)); + +#if defined(GVARS) && !defined(PCBSTD) + static const pm_uint8_t mstate_tab_fm1[] PROGMEM = {0, 0, 0, (uint8_t)-1, 1, 1, 1, 1, 1}; + static const pm_uint8_t mstate_tab_others[] PROGMEM = {0, 0, 3, IF_ROTARY_ENCODERS(NUM_ROTARY_ENCODERS-1) 0, 0, (uint8_t)-1, 2, 2, 2, 2, 2}; + + check(event, 0, NULL, 0, (s_currIdx == 0) ? mstate_tab_fm1 : mstate_tab_others, DIM(mstate_tab_others)-1, ITEM_MODEL_PHASE_MAX - 1 - (s_currIdx==0 ? (ITEM_MODEL_PHASE_FADE_IN-ITEM_MODEL_PHASE_SWITCH) : 0)); + + TITLE(STR_MENUFLIGHTPHASE); + + #define PHASE_ONE_FIRST_LINE (1+1*FH) +#else + SUBMENU(STR_MENUFLIGHTPHASE, 3 + (s_currIdx==0 ? 0 : 2 + (bool)NUM_ROTARY_ENCODERS), {0, 0, 3, IF_ROTARY_ENCODERS(NUM_ROTARY_ENCODERS-1) 0/*, 0*/}); + #define PHASE_ONE_FIRST_LINE (1+1*FH) +#endif + + int8_t sub = m_posVert; + int8_t editMode = s_editMode; + +#if defined(GVARS) && !defined(PCBSTD) + if (s_currIdx == 0 && sub>=ITEM_MODEL_PHASE_SWITCH) sub += ITEM_MODEL_PHASE_FADE_IN-ITEM_MODEL_PHASE_SWITCH; + + for (uint8_t k=0; k=ITEM_MODEL_PHASE_SWITCH) i += ITEM_MODEL_PHASE_FADE_IN-ITEM_MODEL_PHASE_SWITCH; + uint8_t attr = (sub==i ? (editMode>0 ? BLINK|INVERS : INVERS) : 0); +#else + for (uint8_t i=0, k=0, y=PHASE_ONE_FIRST_LINE; i0 ? BLINK|INVERS : INVERS) : 0); +#endif + switch(i) { + case ITEM_MODEL_PHASE_NAME: + editSingleName(MIXES_2ND_COLUMN, y, STR_PHASENAME, fm->name, sizeof(fm->name), event, attr); + break; + case ITEM_MODEL_PHASE_SWITCH: + fm->swtch = switchMenuItem(MIXES_2ND_COLUMN, y, fm->swtch, attr, event); + break; + case ITEM_MODEL_PHASE_TRIMS: + lcd_putsLeft(y, STR_TRIMS); + for (uint8_t t=0; t0) || p1valdiff)) { + int16_t v = getRawTrimValue(s_currIdx, t); + if (v < TRIM_EXTENDED_MAX) v = TRIM_EXTENDED_MAX; + v = checkIncDec(event, v, TRIM_EXTENDED_MAX, TRIM_EXTENDED_MAX+MAX_FLIGHT_MODES-1, EE_MODEL); + if (checkIncDec_Ret) { + if (v == TRIM_EXTENDED_MAX) v = 0; + setTrimValue(s_currIdx, t, v); + } + } + } + break; + +#if ROTARY_ENCODERS > 0 + case ITEM_MODEL_PHASE_ROTARY_ENCODERS: + lcd_putsLeft(y, STR_ROTARY_ENCODER); + for (uint8_t t=0; t0) || p1valdiff)) { + int16_t v = flightModeAddress(s_currIdx)->rotaryEncoders[t]; + if (v < ROTARY_ENCODER_MAX) v = ROTARY_ENCODER_MAX; + v = checkIncDec(event, v, ROTARY_ENCODER_MAX, ROTARY_ENCODER_MAX+MAX_FLIGHT_MODES-1, EE_MODEL); + if (checkIncDec_Ret) { + if (v == ROTARY_ENCODER_MAX) v = 0; + flightModeAddress(s_currIdx)->rotaryEncoders[t] = v; + } + } + } + break; +#endif + + case ITEM_MODEL_PHASE_FADE_IN: + fm->fadeIn = EDIT_DELAY(0, y, event, attr, STR_FADEIN, fm->fadeIn); + break; + + case ITEM_MODEL_PHASE_FADE_OUT: + fm->fadeOut = EDIT_DELAY(0, y, event, attr, STR_FADEOUT, fm->fadeOut); + break; + +#if defined(GVARS) && !defined(PCBSTD) + case ITEM_MODEL_PHASE_GVARS_LABEL: + lcd_putsLeft(y, STR_GLOBAL_VARS); + break; + + default: + { + uint8_t idx = i-ITEM_MODEL_PHASE_GV1; + uint8_t posHorz = m_posHorz; + if (attr && posHorz > 0 && s_currIdx==0) posHorz++; + + putsStrIdx(INDENT_WIDTH, y, STR_GV, idx+1); + + editName(4*FW, y, g_model.gvars[idx].name, LEN_GVAR_NAME, event, posHorz==0 ? attr : 0); + + int16_t v = fm->gvars[idx]; + if (v > GVAR_MAX) { + uint8_t p = v - GVAR_MAX - 1; + if (p >= s_currIdx) p++; + putsFlightMode(11*FW, y, p+1, posHorz==1 ? attr : 0); + } + else { + lcd_putsAtt(11*FW, y, STR_OWN, posHorz==1 ? attr : 0); + } + if (attr && s_currIdx>0 && posHorz==1 && (editMode>0 || p1valdiff)) { + if (v < GVAR_MAX) v = GVAR_MAX; + v = checkIncDec(event, v, GVAR_MAX, GVAR_MAX+MAX_FLIGHT_MODES-1, EE_MODEL); + if (checkIncDec_Ret) { + if (v == GVAR_MAX) v = 0; + fm->gvars[idx] = v; + } + } + + uint8_t p = getGVarFlightPhase(s_currIdx, idx); + lcd_outdezAtt(21*FW, y, GVAR_VALUE(idx, p), posHorz==2 ? attr : 0); + if (attr && posHorz==2 && ((editMode>0) || p1valdiff)) { + GVAR_VALUE(idx, p) = checkIncDec(event, GVAR_VALUE(idx, p), -GVAR_LIMIT, GVAR_LIMIT, EE_MODEL); + } + + break; + } +#endif + } + } +} + +#if defined(ROTARY_ENCODERS) + #if ROTARY_ENCODERS > 2 + #define NAME_OFS (-4-12) + #define SWITCH_OFS (-FW/2-2-13) + #define TRIMS_OFS (-FW/2-4-15) + #define ROTARY_ENC_OFS (0) + #else + #define NAME_OFS (-4) + #define SWITCH_OFS (-FW/2-2) + #define TRIMS_OFS (-FW/2-4) + #define ROTARY_ENC_OFS (2) + #endif +#else + #define NAME_OFS 0 + #define SWITCH_OFS (FW/2) + #define TRIMS_OFS (FW/2) +#endif + +void menuModelFlightModesAll(uint8_t event) +{ + SIMPLE_MENU(STR_MENUFLIGHTPHASES, menuTabModel, e_FlightModesAll, 1+MAX_FLIGHT_MODES+1); + + int8_t sub = m_posVert - 1; + + switch (event) { + CASE_EVT_ROTARY_BREAK + case EVT_KEY_FIRST(KEY_ENTER): + if (sub == MAX_FLIGHT_MODES) { + s_editMode = 0; + trimsCheckTimer = 200; // 2 seconds + } + // no break + case EVT_KEY_FIRST(KEY_RIGHT): + if (sub >= 0 && sub < MAX_FLIGHT_MODES) { + s_currIdx = sub; + pushMenu(menuModelPhaseOne); + } + break; + } + + uint8_t att; + for (uint8_t i=0; i(LCD_LINES-1)*FH+MENU_TITLE_HEIGHT-FH) continue; +#else + coord_t y = MENU_TITLE_HEIGHT + 1 + i*FH; +#endif + att = (i==sub ? INVERS : 0); + FlightModeData *p = flightModeAddress(i); + putsFlightMode(0, y, i+1, att|(getFlightMode()==i ? BOLD : 0)); + + lcd_putsnAtt(4*FW+NAME_OFS, y, p->name, sizeof(p->name), ZCHAR); + if (i == 0) { + lcd_puts((5+LEN_FLIGHT_MODE_NAME)*FW+SWITCH_OFS, y, STR_DEFAULT); + } + else { + putsSwitches((5+LEN_FLIGHT_MODE_NAME)*FW+SWITCH_OFS, y, p->swtch, 0); + for (uint8_t t=0; tfadeIn || p->fadeOut) + lcd_putc(LCD_W-FW-MENUS_SCROLLBAR_WIDTH, y, (p->fadeIn && p->fadeOut) ? '*' : (p->fadeIn ? 'I' : 'O')); + + } + +#if defined(CPUARM) + if (s_pgOfs != MAX_FLIGHT_MODES-(LCD_LINES-2)) return; +#endif + + lcd_putsLeft((LCD_LINES-1)*FH+1, STR_CHECKTRIMS); + putsFlightMode(OFS_CHECKTRIMS, (LCD_LINES-1)*FH+1, mixerCurrentFlightMode+1); + if (sub==MAX_FLIGHT_MODES && !trimsCheckTimer) { + lcd_status_line(); + } +} + +#endif // defined(PCBTARANIS) diff --git a/radio/src/gui/menu_model_heli.cpp b/radio/src/gui/menu_model_heli.cpp new file mode 100755 index 000000000..b1252ef3f --- /dev/null +++ b/radio/src/gui/menu_model_heli.cpp @@ -0,0 +1,96 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "../opentx.h" + +enum menuModelHeliItems { + ITEM_HELI_SWASHTYPE, + ITEM_HELI_COLLECTIVE, + ITEM_HELI_SWASHRING, + ITEM_HELI_ELEDIRECTION, + ITEM_HELI_AILDIRECTION, + ITEM_HELI_COLDIRECTION +}; + +#if LCD_W >= 212 + #define HELI_PARAM_OFS (23*FW) +#else + #define HELI_PARAM_OFS (14*FW) +#endif + +void menuModelHeli(uint8_t event) +{ + SIMPLE_MENU(STR_MENUHELISETUP, menuTabModel, e_Heli, 7); + + uint8_t sub = m_posVert - 1; + + for (uint8_t i=0; i<6; i++) { + coord_t y = MENU_TITLE_HEIGHT + 1 + i*FH; + uint8_t attr = (sub == i ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0); + switch(i) { + case ITEM_HELI_SWASHTYPE: + g_model.swashR.type = selectMenuItem(HELI_PARAM_OFS, y, STR_SWASHTYPE, STR_VSWASHTYPE, g_model.swashR.type, 0, SWASH_TYPE_MAX, attr, event); + break; + + case ITEM_HELI_COLLECTIVE: +#if defined(PCBTARANIS) + lcd_putsLeft(y, STR_COLLECTIVE); + if (attr) CHECK_INCDEC_MODELSOURCE(event, g_model.swashR.collectiveSource, 0, MIXSRC_LAST_CH); +#else + g_model.swashR.collectiveSource = selectMenuItem(HELI_PARAM_OFS, y, STR_COLLECTIVE, NULL, g_model.swashR.collectiveSource, 0, MIXSRC_LAST_CH, attr, event); +#endif + putsMixerSource(HELI_PARAM_OFS, y, g_model.swashR.collectiveSource, attr); + break; + + case ITEM_HELI_SWASHRING: + lcd_putsLeft(y, STR_SWASHRING); + lcd_outdezAtt(HELI_PARAM_OFS, y, g_model.swashR.value, LEFT|attr); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.swashR.value, 100); + break; + + case ITEM_HELI_ELEDIRECTION: + g_model.swashR.invertELE = selectMenuItem(HELI_PARAM_OFS, y, STR_ELEDIRECTION, STR_MMMINV, g_model.swashR.invertELE, 0, 1, attr, event); + break; + + case ITEM_HELI_AILDIRECTION: + g_model.swashR.invertAIL = selectMenuItem(HELI_PARAM_OFS, y, STR_AILDIRECTION, STR_MMMINV, g_model.swashR.invertAIL, 0, 1, attr, event); + break; + + case ITEM_HELI_COLDIRECTION: + g_model.swashR.invertCOL = selectMenuItem(HELI_PARAM_OFS, y, STR_COLDIRECTION, STR_MMMINV, g_model.swashR.invertCOL, 0, 1, attr, event); + break; + } + } +} diff --git a/radio/src/gui/menu_model_inputs_mixes.cpp b/radio/src/gui/menu_model_inputs_mixes.cpp new file mode 100755 index 000000000..6ee6033e0 --- /dev/null +++ b/radio/src/gui/menu_model_inputs_mixes.cpp @@ -0,0 +1,1275 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "../opentx.h" + +#if LCD_W >= 212 + #define EXPO_ONE_2ND_COLUMN (LCD_W-8*FW-90) + #define EXPO_ONE_FM_WIDTH (9*FW) +#else + #define EXPO_ONE_2ND_COLUMN (7*FW+3*FW+2) + #define EXPO_ONE_FM_WIDTH (5*FW) +#endif + +#if defined(FLIGHT_MODES) +void displayFlightModes(coord_t x, coord_t y, FlightModesType value); +FlightModesType editFlightModes(coord_t x, coord_t y, uint8_t event, FlightModesType value, uint8_t attr) +{ + lcd_putsColumnLeft(x, y, STR_FLMODE); + + uint8_t posHorz = m_posHorz; + +#if defined(CPUARM) && LCD_W < 212 + bool expoMenu = (x==EXPO_ONE_2ND_COLUMN-5*FW); +#endif + + for (uint8_t p=0; p EXPO_ONE_2ND_COLUMN-FW))) + continue; +#endif +#if defined(PCBTARANIS) + LcdFlags flags = 0; + if (attr) { + flags |= INVERS; + if (posHorz==p) flags |= BLINK; + } + if (value & (1<srcRaw, x); +#else + anas[ed->chn] = x; + applyExpos(anas, e_perout_mode_inactive_flight_mode); +#endif + return anas[ed->chn]; +} + +void DrawFunction(FnFuncP fn, uint8_t offset=0) +{ + lcd_vlineStip(X0-offset, 0, LCD_H, 0xee); + lcd_hlineStip(X0-WCHART-offset, Y0, WCHART*2, 0xee); + + coord_t prev_yv = (coord_t)-1; + + for (int8_t xv=-WCHART; xv<=WCHART; xv++) { + coord_t yv = (LCD_H-1) - (((uint16_t)RESX + fn(xv * (RESX/WCHART))) / 2 * (LCD_H-1) / RESX); + if (prev_yv != (coord_t)-1) { + if (abs((int8_t)yv-prev_yv) <= 1) { + lcd_plot(X0+xv-offset-1, prev_yv, FORCE); + } + else { + uint8_t tmp = (prev_yv < yv ? 0 : 1); + lcd_vline(X0+xv-offset-1, yv+tmp, prev_yv-yv); + } + } + prev_yv = yv; + } +} + + +uint8_t getExpoMixCount(uint8_t expo) +{ + uint8_t count = 0; + uint8_t ch ; + + for(int8_t i=(expo ? MAX_EXPOS-1 : MAX_MIXERS-1); i>=0; i--) { + ch = (expo ? EXPO_VALID(expoAddress(i)) : mixAddress(i)->srcRaw); + if (ch != 0) { + count++; + } + } + return count; +} + +bool reachExpoMixCountLimit(uint8_t expo) +{ + // check mixers count limit + if (getExpoMixCount(expo) >= (expo ? MAX_EXPOS : MAX_MIXERS)) { + POPUP_WARNING(expo ? STR_NOFREEEXPO : STR_NOFREEMIXER); + return true; + } + return false; +} + +void deleteExpoMix(uint8_t expo, uint8_t idx) +{ + pauseMixerCalculations(); + if (expo) { + ExpoData *expo = expoAddress(idx); +#if defined(PCBTARANIS) + int input = expo->chn; +#endif + memmove(expo, expo+1, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); + memclear(&g_model.expoData[MAX_EXPOS-1], sizeof(ExpoData)); +#if defined(PCBTARANIS) + if (!isInputAvailable(input)) { + memclear(&g_model.inputNames[input], LEN_INPUT_NAME); + } +#endif + } + else { + MixData *mix = mixAddress(idx); + memmove(mix, mix+1, (MAX_MIXERS-(idx+1))*sizeof(MixData)); + memclear(&g_model.mixData[MAX_MIXERS-1], sizeof(MixData)); + } + resumeMixerCalculations(); + eeDirty(EE_MODEL); +} + +// TODO avoid this global s_currCh on ARM boards ... +int8_t s_currCh; +void insertExpoMix(uint8_t expo, uint8_t idx) +{ + pauseMixerCalculations(); + if (expo) { + ExpoData *expo = expoAddress(idx); + memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); + memclear(expo, sizeof(ExpoData)); +#if defined(PCBTARANIS) + expo->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channel_order(s_currCh)); + expo->curve.type = CURVE_REF_EXPO; +#endif + expo->mode = 3; // pos&neg + expo->chn = s_currCh - 1; + expo->weight = 100; + } + else { + MixData *mix = mixAddress(idx); + memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData)); + memclear(mix, sizeof(MixData)); + mix->destCh = s_currCh-1; +#if defined(PCBTARANIS) + mix->srcRaw = s_currCh; + if (!isSourceAvailable(mix->srcRaw)) + mix->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channel_order(s_currCh)); +#else + mix->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channel_order(s_currCh)); +#endif + mix->weight = 100; + } + resumeMixerCalculations(); + eeDirty(EE_MODEL); +} + +void copyExpoMix(uint8_t expo, uint8_t idx) +{ + pauseMixerCalculations(); + if (expo) { + ExpoData *expo = expoAddress(idx); + memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); + } + else { + MixData *mix = mixAddress(idx); + memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData)); + } + resumeMixerCalculations(); + eeDirty(EE_MODEL); +} + +void memswap(void *a, void *b, uint8_t size) +{ + uint8_t *x = (uint8_t*)a; + uint8_t *y = (uint8_t*)b; + uint8_t temp ; + + while (size--) { + temp = *x; + *x++ = *y; + *y++ = temp; + } +} + +bool swapExpoMix(uint8_t expo, uint8_t &idx, uint8_t up) +{ + void *x, *y; + uint8_t size; + int8_t tgt_idx = (up ? idx-1 : idx+1); + + if (expo) { + x = (ExpoData *)expoAddress(idx); + + if (tgt_idx < 0) { + if (((ExpoData *)x)->chn == 0) + return false; + ((ExpoData *)x)->chn--; + return true; + } + + if (tgt_idx == MAX_EXPOS) { + if (((ExpoData *)x)->chn == NUM_INPUTS-1) + return false; + ((ExpoData *)x)->chn++; + return true; + } + + y = (ExpoData *)expoAddress(tgt_idx); + if(((ExpoData *)x)->chn != ((ExpoData *)y)->chn || !EXPO_VALID((ExpoData *)y)) { + if (up) { + if (((ExpoData *)x)->chn>0) ((ExpoData *)x)->chn--; + else return false; + } + else { + if (((ExpoData *)x)->chnchn++; + else return false; + } + return true; + } + + size = sizeof(ExpoData); + } + else { + x = (MixData *)mixAddress(idx); + + if (tgt_idx < 0) { + if (((MixData *)x)->destCh == 0) + return false; + ((MixData *)x)->destCh--; + return true; + } + + if (tgt_idx == MAX_MIXERS) { + if (((MixData *)x)->destCh == NUM_CHNOUT-1) + return false; + ((MixData *)x)->destCh++; + return true; + } + + y = (MixData *)mixAddress(tgt_idx); + uint8_t destCh = ((MixData *)x)->destCh; + if(!((MixData *)y)->srcRaw || destCh != ((MixData *)y)->destCh) { + if (up) { + if (destCh>0) ((MixData *)x)->destCh--; + else return false; + } + else { + if (destChdestCh++; + else return false; + } + return true; + } + + size = sizeof(MixData); + } + + pauseMixerCalculations(); + memswap(x, y, size); + resumeMixerCalculations(); + + idx = tgt_idx; + return true; +} + +enum ExposFields { + CASE_PCBTARANIS(EXPO_FIELD_INPUT_NAME) + CASE_CPUARM(EXPO_FIELD_NAME) + CASE_PCBTARANIS(EXPO_FIELD_SOURCE) + CASE_PCBTARANIS(EXPO_FIELD_SCALE) + EXPO_FIELD_WEIGHT, + CASE_PCBTARANIS(EXPO_FIELD_OFFSET) + CASE_9X(EXPO_FIELD_EXPO) + CASE_CURVES(EXPO_FIELD_CURVE) + CASE_FLIGHT_MODES(EXPO_FIELD_FLIGHT_MODES) + EXPO_FIELD_SWITCH, + EXPO_FIELD_SIDE, + CASE_PCBTARANIS(EXPO_FIELD_TRIM) + EXPO_FIELD_MAX +}; + +#if defined(PCBTARANIS) + #define CURVE_ROWS 1 +#else + #define CURVE_ROWS 0 +#endif + +void menuModelExpoOne(uint8_t event) +{ +#if defined(PCBTARANIS) + if (event == EVT_KEY_LONG(KEY_MENU)) { + pushMenu(menuChannelsView); + killEvents(event); + } +#endif + + ExpoData *ed = expoAddress(s_currIdx); +#if defined(PCBTARANIS) + putsMixerSource(7*FW+FW/2, 0, MIXSRC_FIRST_INPUT+ed->chn, 0); +#else + putsMixerSource(7*FW+FW/2, 0, MIXSRC_Rud+ed->chn, 0); +#endif + + SUBMENU(STR_MENUINPUTS, EXPO_FIELD_MAX, {CASE_PCBTARANIS(0) CASE_CPUARM(0) CASE_PCBTARANIS(0) CASE_PCBTARANIS((ed->srcRaw >= MIXSRC_FIRST_TELEM ? (uint8_t)0 : (uint8_t)HIDDEN_ROW)) 0, CASE_PCBTARANIS(0) CASE_9X(0) CASE_CURVES(CURVE_ROWS) CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0 /*, ...*/}); + + SET_SCROLLBAR_X(EXPO_ONE_2ND_COLUMN+10*FW); + + int8_t sub = m_posVert; + + coord_t y = MENU_TITLE_HEIGHT + 1; + +#if defined(PCBTARANIS) + for (unsigned int k=0; k0 ? BLINK|INVERS : INVERS) : 0); + switch(i) + { +#if defined(PCBTARANIS) + case EXPO_FIELD_INPUT_NAME: + editSingleName(EXPO_ONE_2ND_COLUMN, y, STR_INPUTNAME, g_model.inputNames[ed->chn], sizeof(g_model.inputNames[ed->chn]), event, attr); + break; +#endif + +#if defined(CPUARM) + case EXPO_FIELD_NAME: + editSingleName(EXPO_ONE_2ND_COLUMN-IF_9X(sizeof(ed->name)*FW), y, STR_EXPONAME, ed->name, sizeof(ed->name), event, attr); + break; +#endif + +#if defined(PCBTARANIS) + case EXPO_FIELD_SOURCE: + lcd_putsLeft(y, NO_INDENT(STR_SOURCE)); + putsMixerSource(EXPO_ONE_2ND_COLUMN, y, ed->srcRaw, STREXPANDED|attr); + if (attr) ed->srcRaw = checkIncDec(event, ed->srcRaw, INPUTSRC_FIRST, INPUTSRC_LAST, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isInputSourceAvailable); + break; + + case EXPO_FIELD_SCALE: + lcd_putsLeft(y, STR_SCALE); + putsTelemetryChannelValue(EXPO_ONE_2ND_COLUMN, y, ed->srcRaw - MIXSRC_FIRST_TELEM, convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale), LEFT|attr); + if (attr) ed->scale = checkIncDec(event, ed->scale, 0, maxTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1), EE_MODEL); + break; +#endif + + case EXPO_FIELD_WEIGHT: + lcd_putsLeft(y, STR_WEIGHT); + ed->weight = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->weight, MIN_EXPO_WEIGHT, 100, IF_PCBTARANIS(LEFT)|attr, 0, event); + break; + +#if defined(PCBTARANIS) + case EXPO_FIELD_OFFSET: + lcd_putsLeft(y, NO_INDENT(STR_OFFSET)); + ed->offset = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->offset, -100, 100, LEFT|attr, 0, event); + break; +#endif + +#if !defined(PCBTARANIS) + case EXPO_FIELD_EXPO: + lcd_putsLeft(y, STR_EXPO); + if (ed->curveMode==MODE_EXPO || ed->curveParam==0) { + ed->curveMode = MODE_EXPO; + ed->curveParam = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->curveParam, -100, 100, attr, 0, event); + } + else { + lcd_putsAtt(EXPO_ONE_2ND_COLUMN-3*FW, y, STR_NA, attr); + } + break; +#endif + +#if defined(CURVES) + case EXPO_FIELD_CURVE: + lcd_putsLeft(y, STR_CURVE); +#if defined(PCBTARANIS) + editCurveRef(EXPO_ONE_2ND_COLUMN, y, ed->curve, event, attr); +#else + if (ed->curveMode!=MODE_EXPO || ed->curveParam==0) { + putsCurve(EXPO_ONE_2ND_COLUMN-3*FW, y, ed->curveParam, attr); + if (attr) { + CHECK_INCDEC_MODELVAR_ZERO(event, ed->curveParam, CURVE_BASE+MAX_CURVES-1); + if (ed->curveParam) ed->curveMode = MODE_CURVE; + if (ed->curveParam>=CURVE_BASE && event==EVT_KEY_LONG(KEY_ENTER)) { + s_curveChan = ed->curveParam - CURVE_BASE; + pushMenu(menuModelCurveOne); + } + } + } + else { + lcd_putsAtt(EXPO_ONE_2ND_COLUMN-3*FW, y, STR_NA, attr); + } +#endif + break; +#endif + +#if defined(FLIGHT_MODES) + case EXPO_FIELD_FLIGHT_MODES: + ed->flightModes = editFlightModes(EXPO_ONE_2ND_COLUMN-IF_9X(EXPO_ONE_FM_WIDTH), y, event, ed->flightModes, attr); + break; +#endif + + case EXPO_FIELD_SWITCH: + ed->swtch = switchMenuItem(EXPO_ONE_2ND_COLUMN-IF_9X(3*FW), y, ed->swtch, attr, event); + break; + + case EXPO_FIELD_SIDE: + ed->mode = 4 - selectMenuItem(EXPO_ONE_2ND_COLUMN-IF_9X(3*FW), y, STR_SIDE, STR_VSIDE, 4-ed->mode, 1, 3, attr, event); + break; + +#if defined(PCBTARANIS) + case EXPO_FIELD_TRIM: + uint8_t not_stick = (ed->srcRaw > MIXSRC_Ail); + int8_t carryTrim = -ed->carryTrim; + lcd_putsLeft(y, STR_TRIM); + lcd_putsiAtt(EXPO_ONE_2ND_COLUMN, y, STR_VMIXTRIMS, (not_stick && carryTrim == 0) ? 0 : carryTrim+1, m_posHorz==0 ? attr : 0); + if (attr) ed->carryTrim = -checkIncDecModel(event, carryTrim, not_stick ? TRIM_ON : -TRIM_OFF, -TRIM_AIL); + break; +#endif + } + y += FH; + } + + DrawFunction(expoFn); + +#if defined(PCBTARANIS) + int x512 = getValue(ed->srcRaw); + if (ed->srcRaw >= MIXSRC_FIRST_TELEM) { + putsTelemetryChannelValue(LCD_W-8, 6*FH, ed->srcRaw - MIXSRC_FIRST_TELEM, x512, 0); + if (ed->scale > 0) x512 = (x512 * 1024) / convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale); + } + else { + lcd_outdezAtt(LCD_W-8, 6*FH, calcRESXto1000(x512), PREC1); + } + x512 = limit(-1024, x512, 1024); + int y512 = expoFn(x512); + y512 = limit(-1024, y512, 1024); + lcd_outdezAtt(LCD_W-8-6*FW, 1*FH, calcRESXto1000(y512), PREC1); +#else + int16_t x512 = calibratedStick[ed->chn]; + lcd_outdezAtt(LCD_W-8, 6*FH, calcRESXto100(x512), 0); + int16_t y512 = expoFn(x512); + lcd_outdezAtt(LCD_W-8-6*FW, 1*FH, calcRESXto100(y512), 0); +#endif + +#if defined(CPUARM) + x512 = X0+x512/(RESX/WCHART); + y512 = (LCD_H-1) - ((y512+RESX)/2) * (LCD_H-1) / RESX; +#else + x512 = X0+x512/(RESXu/WCHART); + y512 = (LCD_H-1) - (uint16_t)((y512+RESX)/2) * (LCD_H-1) / RESX; +#endif + + lcd_vline(x512, y512-3, 3*2+1); + lcd_hline(x512-3, y512, 3*2+1); +} + +enum MixFields { + CASE_CPUARM(MIX_FIELD_NAME) + MIX_FIELD_SOURCE, + MIX_FIELD_WEIGHT, + MIX_FIELD_OFFSET, + MIX_FIELD_TRIM, + CASE_CURVES(MIX_FIELD_CURVE) + CASE_FLIGHT_MODES(MIX_FIELD_FLIGHT_PHASE) + MIX_FIELD_SWITCH, + MIX_FIELD_WARNING, + MIX_FIELD_MLTPX, + MIX_FIELD_DELAY_UP, + MIX_FIELD_DELAY_DOWN, + MIX_FIELD_SLOW_UP, + MIX_FIELD_SLOW_DOWN, + MIX_FIELD_COUNT +}; + +void gvarWeightItem(coord_t x, coord_t y, MixData *md, uint8_t attr, uint8_t event) +{ + u_int8int16_t weight; + MD_WEIGHT_TO_UNION(md, weight); + weight.word = GVAR_MENU_ITEM(x, y, weight.word, GV_RANGELARGE_WEIGHT_NEG, GV_RANGELARGE_WEIGHT, attr, 0, event); + MD_UNION_TO_WEIGHT(weight, md); +} + +#if !defined(CPUM64) || !defined(FRSKY) +#define GAUGE_WIDTH 33 +#define GAUGE_HEIGHT 6 +void drawOffsetBar(uint8_t x, uint8_t y, MixData * md) +{ + int offset = GET_GVAR(MD_OFFSET(md), GV_RANGELARGE_NEG, GV_RANGELARGE, mixerCurrentFlightMode); + int weight = abs(GET_GVAR(MD_WEIGHT(md), GV_RANGELARGE_NEG, GV_RANGELARGE, mixerCurrentFlightMode)); + int barMin = offset - weight; + int barMax = offset + weight; + if (y > 15) { +#if defined(CPUARM) + lcd_outdezAtt(x-((barMin >= 0) ? 2 : 3), y-6, barMin, TINSIZE|LEFT); + lcd_outdezAtt(x+GAUGE_WIDTH+1, y-6, barMax, TINSIZE); +#else + lcd_outdezAtt(x-((barMin >= 0) ? 2 : 3), y-8, barMin, LEFT); + lcd_outdezAtt(x+GAUGE_WIDTH+1, y-8, barMax); +#endif + } + if (barMin < -101) + barMin = -101; + if (barMax > 101) + barMax = 101; + lcd_hlineStip(x-2, y, GAUGE_WIDTH+2, DOTTED); + lcd_hlineStip(x-2, y+GAUGE_HEIGHT, GAUGE_WIDTH+2, DOTTED); + lcd_vline(x-2, y+1, GAUGE_HEIGHT-1); + lcd_vline(x+GAUGE_WIDTH-1, y+1, GAUGE_HEIGHT-1); + if (barMin <= barMax) { + int8_t right = (barMax * GAUGE_WIDTH) / 200; + int8_t left = ((barMin * GAUGE_WIDTH) / 200)-1; + lcd_filled_rect(x+GAUGE_WIDTH/2+left, y+2, right-left, GAUGE_HEIGHT-3); + } + lcd_vline(x+GAUGE_WIDTH/2-1, y, GAUGE_HEIGHT+1); + if (barMin == -101) { + for (uint8_t i=0; i<3; ++i) { + lcd_plot(x+i, y+4-i); + lcd_plot(x+3+i, y+4-i); + } + } + if (barMax == 101) { + for (uint8_t i=0; i<3; ++i) { + lcd_plot(x+GAUGE_WIDTH-8+i, y+4-i); + lcd_plot(x+GAUGE_WIDTH-5+i, y+4-i); + } + } +} +#undef GAUGE_WIDTH +#undef GAUGE_HEIGHT +#endif + +void menuModelMixOne(uint8_t event) +{ +#if defined(PCBTARANIS) + if (event == EVT_KEY_LONG(KEY_MENU)) { + pushMenu(menuChannelsView); + killEvents(event); + } +#endif + + TITLE(s_currCh ? STR_INSERTMIX : STR_EDITMIX); + MixData *md2 = mixAddress(s_currIdx) ; + putsChn(lcdLastPos+1*FW, 0, md2->destCh+1,0); + +#if defined(ROTARY_ENCODERS) +#if defined(CURVES) + if ((m_posVert == MIX_FIELD_TRIM && md2->srcRaw > NUM_STICKS) || (m_posVert == MIX_FIELD_CURVE && md2->curveMode == MODE_CURVE)) +#else + if (m_posVert == MIX_FIELD_TRIM && md2->srcRaw > NUM_STICKS) +#endif + SUBMENU_NOTITLE(MIX_FIELD_COUNT, {CASE_CPUARM(0) 0, 0, 0, CASE_9X(0) CASE_CURVES(0) CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0, 0 /*, ...*/}) + else + SUBMENU_NOTITLE(MIX_FIELD_COUNT, {CASE_CPUARM(0) 0, 0, 0, CASE_9X(1) CASE_CURVES(1) CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0, 0 /*, ...*/}); +#else + SUBMENU_NOTITLE(MIX_FIELD_COUNT, {CASE_CPUARM(0) 0, 0, 0, CASE_9X(1) CASE_PCBTARANIS(0) CASE_CURVES(1) CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0, 0 /*, ...*/}); +#endif + +#if MENU_COLUMNS > 1 + lcd_vline(MENU_COLUMN2_X-4, FH+1, LCD_H-FH-1); +#endif + + int8_t sub = m_posVert; + int8_t editMode = s_editMode; + + for (uint8_t k=0; k 1 + coord_t y; + coord_t COLUMN_X; + if (k >= LCD_LINES-1) { + y = 1 + (k-LCD_LINES+2)*FH; + COLUMN_X = MENU_COLUMN2_X; + } + else { + y = 1 + (k+1)*FH; + COLUMN_X = 0; + } + int8_t i = k; +#else + coord_t y = MENU_TITLE_HEIGHT + 1 + k*FH; + int8_t i = k + s_pgOfs; +#endif + + uint8_t attr = (sub==i ? (editMode>0 ? BLINK|INVERS : INVERS) : 0); + switch(i) { +#if defined(CPUARM) + case MIX_FIELD_NAME: + editSingleName(COLUMN_X+MIXES_2ND_COLUMN, y, STR_MIXNAME, md2->name, sizeof(md2->name), event, attr); + break; +#endif + case MIX_FIELD_SOURCE: + lcd_putsColumnLeft(COLUMN_X, y, NO_INDENT(STR_SOURCE)); + putsMixerSource(COLUMN_X+MIXES_2ND_COLUMN, y, md2->srcRaw, STREXPANDED|attr); + if (attr) CHECK_INCDEC_MODELSOURCE(event, md2->srcRaw, 1, MIXSRC_LAST); + break; + case MIX_FIELD_WEIGHT: + lcd_putsColumnLeft(COLUMN_X, y, STR_WEIGHT); + gvarWeightItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2, attr|LEFT, event); + break; + case MIX_FIELD_OFFSET: + { + lcd_putsColumnLeft(COLUMN_X, y, NO_INDENT(STR_OFFSET)); + u_int8int16_t offset; + MD_OFFSET_TO_UNION(md2, offset); + offset.word = GVAR_MENU_ITEM(COLUMN_X+MIXES_2ND_COLUMN, y, offset.word, GV_RANGELARGE_OFFSET_NEG, GV_RANGELARGE_OFFSET, attr|LEFT, 0, event); + MD_UNION_TO_OFFSET(offset, md2); +#if !defined(CPUM64) || !defined(FRSKY) + drawOffsetBar(COLUMN_X+MIXES_2ND_COLUMN+22, y, md2); +#endif + break; + } + +#if defined(PCBTARANIS) + case MIX_FIELD_TRIM: + lcd_putsColumnLeft(COLUMN_X, y, STR_TRIM); + menu_lcd_onoff(COLUMN_X+MIXES_2ND_COLUMN, y, !md2->carryTrim, attr); + if (attr) md2->carryTrim = !checkIncDecModel(event, !md2->carryTrim, 0, 1); + break; +#else + case MIX_FIELD_TRIM: + { + uint8_t not_stick = (md2->srcRaw > NUM_STICKS); + int8_t carryTrim = -md2->carryTrim; + lcd_putsColumnLeft(COLUMN_X, y, STR_TRIM); + lcd_putsiAtt((not_stick ? COLUMN_X+MIXES_2ND_COLUMN : COLUMN_X+6*FW-3), y, STR_VMIXTRIMS, (not_stick && carryTrim == 0) ? 0 : carryTrim+1, m_posHorz==0 ? attr : 0); + if (attr && m_posHorz==0 && (not_stick || editMode>0)) md2->carryTrim = -checkIncDecModel(event, carryTrim, not_stick ? TRIM_ON : -TRIM_OFF, -TRIM_AIL); + if (!not_stick) { + lcd_puts(COLUMN_X+MIXES_2ND_COLUMN, y, STR_DREX); + menu_lcd_onoff(COLUMN_X+MIXES_2ND_COLUMN+DREX_CHBOX_OFFSET, y, !md2->noExpo, m_posHorz==1 ? attr : 0); + if (attr && m_posHorz==1 && editMode>0) md2->noExpo = !checkIncDecModel(event, !md2->noExpo, 0, 1); + } + else if (attr) { + REPEAT_LAST_CURSOR_MOVE(); + } + break; + } +#endif + +#if defined(CURVES) + case MIX_FIELD_CURVE: + { + lcd_putsColumnLeft(COLUMN_X, y, STR_CURVE); +#if defined(PCBTARANIS) + editCurveRef(COLUMN_X+MIXES_2ND_COLUMN, y, md2->curve, event, attr); +#else + int8_t curveParam = md2->curveParam; + if (md2->curveMode == MODE_CURVE) { + putsCurve(COLUMN_X+MIXES_2ND_COLUMN, y, curveParam, attr); + if (attr) { + if (event==EVT_KEY_LONG(KEY_ENTER) && (curveParam<0 || curveParam>=CURVE_BASE)){ + s_curveChan = (curveParam<0 ? -curveParam-1 : curveParam-CURVE_BASE); + pushMenu(menuModelCurveOne); + } + else { + CHECK_INCDEC_MODELVAR(event, md2->curveParam, -MAX_CURVES, CURVE_BASE+MAX_CURVES-1); + if (md2->curveParam == 0) + md2->curveMode = MODE_DIFFERENTIAL; +#if defined(ROTARY_ENCODER_NAVIGATION) || defined(PCBTARANIS) + MOVE_CURSOR_FROM_HERE(); +#else + m_posHorz = 0; +#endif + } + } + } + else { + lcd_putsAtt(COLUMN_X+MIXES_2ND_COLUMN, y, PSTR("Diff"), m_posHorz==0 ? attr : 0); + md2->curveParam = GVAR_MENU_ITEM(COLUMN_X+MIXES_2ND_COLUMN+5*FW, y, curveParam, -100, 100, LEFT|(m_posHorz==1 ? attr : 0), 0, editMode>0 ? event : 0); + if (attr && editMode>0 && m_posHorz==0) { + int8_t tmp = 0; + CHECK_INCDEC_MODELVAR(event, tmp, -1, 1); + if (tmp != 0) { + md2->curveMode = MODE_CURVE; + md2->curveParam = tmp; + } + } + } +#endif + break; + } +#endif +#if defined(FLIGHT_MODES) + case MIX_FIELD_FLIGHT_PHASE: + md2->flightModes = editFlightModes(COLUMN_X+MIXES_2ND_COLUMN, y, event, md2->flightModes, attr); + break; +#endif + case MIX_FIELD_SWITCH: + md2->swtch = switchMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2->swtch, attr, event); + break; + case MIX_FIELD_WARNING: + lcd_putsColumnLeft(COLUMN_X+MIXES_2ND_COLUMN, y, STR_MIXWARNING); + if (md2->mixWarn) + lcd_outdezAtt(COLUMN_X+MIXES_2ND_COLUMN, y, md2->mixWarn, attr|LEFT); + else + lcd_putsAtt(COLUMN_X+MIXES_2ND_COLUMN, y, STR_OFF, attr); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, md2->mixWarn, 3); + break; + case MIX_FIELD_MLTPX: + md2->mltpx = selectMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, STR_MULTPX, STR_VMLTPX, md2->mltpx, 0, 2, attr, event); + break; + case MIX_FIELD_DELAY_UP: + md2->delayUp = EDIT_DELAY(COLUMN_X, y, event, attr, STR_DELAYUP, md2->delayUp); + break; + case MIX_FIELD_DELAY_DOWN: + md2->delayDown = EDIT_DELAY(COLUMN_X, y, event, attr, STR_DELAYDOWN, md2->delayDown); + break; + case MIX_FIELD_SLOW_UP: + md2->speedUp = EDIT_DELAY(COLUMN_X, y, event, attr, STR_SLOWUP, md2->speedUp); + break; + case MIX_FIELD_SLOW_DOWN: + md2->speedDown = EDIT_DELAY(COLUMN_X, y, event, attr, STR_SLOWDOWN, md2->speedDown); + break; + } + } +} + +static uint8_t s_maxLines = 8; +static uint8_t s_copySrcIdx; +static uint8_t s_copySrcCh; + +#define _STR_MAX(x) PSTR("/" #x) +#define STR_MAX(x) _STR_MAX(x) + +#if LCD_W >= 212 + #define EXPO_LINE_WEIGHT_POS 8*FW+8 + #define EXPO_LINE_SRC_POS 9*FW+3 + #define EXPO_LINE_CURVE_POS 12*FW+11 + #define EXPO_LINE_TRIM_POS 19*FW-4 + #define EXPO_LINE_SWITCH_POS 20*FW-1 + #define EXPO_LINE_SIDE_POS 25*FW + #define EXPO_LINE_FM_POS 12*FW+11 + #define EXPO_LINE_SELECT_POS 5*FW+2 + #define EXPO_LINE_NAME_POS LCD_W-LEN_EXPOMIX_NAME*FW-MENUS_SCROLLBAR_WIDTH + #define MIX_LINE_WEIGHT_POS 6*FW+8 + #define MIX_LINE_SRC_POS 7*FW+3 + #define MIX_LINE_CURVE_POS 13*FW+3 + #define MIX_LINE_SWITCH_POS 19*FW+1 + #define MIX_LINE_FM_POS 13*FW+3 + #define MIX_LINE_DELAY_POS 24*FW+3 +#elif defined(CPUARM) + #define EXPO_LINE_WEIGHT_POS 7*FW+1 + #define EXPO_LINE_EXPO_POS 10*FW+5 + #define EXPO_LINE_SWITCH_POS 11*FW+2 + #define EXPO_LINE_SIDE_POS 14*FW+2 + #define EXPO_LINE_SELECT_POS 24 + #define EXPO_LINE_FM_POS + #define EXPO_LINE_NAME_POS LCD_W-LEN_EXPOMIX_NAME*FW-MENUS_SCROLLBAR_WIDTH + #define MIX_LINE_SRC_POS 4*FW-1 + #define MIX_LINE_WEIGHT_POS 11*FW+3 + #define MIX_LINE_CURVE_POS 12*FW+2 + #define MIX_LINE_SWITCH_POS 16*FW + #define MIX_LINE_DELAY_POS 19*FW+7 +#else + #define EXPO_LINE_WEIGHT_POS 7*FW+1 + #define EXPO_LINE_EXPO_POS 11*FW + #define EXPO_LINE_SWITCH_POS 11*FW+4 + #if MAX_FLIGHT_MODES == 6 + #define EXPO_LINE_SIDE_POS 15*FW + #else + #define EXPO_LINE_SIDE_POS 15*FW+2 + #endif + #define EXPO_LINE_FM_POS LCD_W-FW-MENUS_SCROLLBAR_WIDTH + #define EXPO_LINE_SELECT_POS 24 + #define MIX_LINE_SRC_POS 4*FW-1 + #define MIX_LINE_WEIGHT_POS 11*FW+3 + #define MIX_LINE_CURVE_POS 12*FW+2 + #define MIX_LINE_SWITCH_POS 16*FW + #define MIX_LINE_DELAY_POS 19*FW+7 +#endif + +#if defined(NAVIGATION_MENUS) +void onExpoMixMenu(const char *result) +{ + bool expo = (g_menuStack[g_menuStackPtr] == menuModelExposAll); + uint8_t chn = (expo ? expoAddress(s_currIdx)->chn+1 : mixAddress(s_currIdx)->destCh+1); + + if (result == STR_EDIT) { + pushMenu(expo ? menuModelExpoOne : menuModelMixOne); + } + else if (result == STR_INSERT_BEFORE || result == STR_INSERT_AFTER) { + if (!reachExpoMixCountLimit(expo)) { + s_currCh = chn; + if (result == STR_INSERT_AFTER) { s_currIdx++; m_posVert++; } + insertExpoMix(expo, s_currIdx); + pushMenu(expo ? menuModelExpoOne : menuModelMixOne); + } + } + else if (result == STR_COPY || result == STR_MOVE) { + s_copyMode = (result == STR_COPY ? COPY_MODE : MOVE_MODE); + s_copySrcIdx = s_currIdx; + s_copySrcCh = chn; + s_copySrcRow = m_posVert; + } + else if (result == STR_DELETE) { + deleteExpoMix(expo, s_currIdx); + } +} +#endif + +#if LCD_W >= 212 +void displayHeaderChannelName(uint8_t ch) +{ + uint8_t len = zlen(g_model.limitData[ch-1].name, sizeof(g_model.limitData[ch-1].name)); + if (len) { + lcd_putc(17*FW, 0, ' '); + lcd_putsnAtt(lcdNextPos, 0, g_model.limitData[ch-1].name, len, ZCHAR); + lcd_putc(lcdNextPos, 0, ' '); + } +} +#endif + +void displayMixInfos(coord_t y, MixData *md) +{ +#if defined(PCBTARANIS) + putsCurveRef(MIX_LINE_CURVE_POS, y, md->curve, 0); +#else + if (md->curveParam) { + if (md->curveMode == MODE_CURVE) + putsCurve(MIX_LINE_CURVE_POS, y, md->curveParam); + else + displayGVar(MIX_LINE_CURVE_POS+3*FW, y, md->curveParam, -100, 100); + } +#endif + + if (md->swtch) { + putsSwitches(MIX_LINE_SWITCH_POS, y, md->swtch); + } +} + +#if defined(PCBTARANIS) +void displayMixLine(coord_t y, MixData *md) +{ + if (md->name[0]) { + lcd_putsnAtt(EXPO_LINE_NAME_POS, y, md->name, sizeof(md->name), ZCHAR); + } + + if (!md->flightModes || ((md->curve.value || md->swtch) && ((get_tmr10ms() / 200) & 1))) + displayMixInfos(y, md); + else + displayFlightModes(MIX_LINE_FM_POS, y, md->flightModes); +} +#elif defined(CPUARM) +void displayMixLine(coord_t y, MixData *md) +{ + if (md->name[0]) { + lcd_putsnAtt(EXPO_LINE_NAME_POS, y, md->name, sizeof(md->name), ZCHAR); + } + else { + displayMixInfos(y, md); + } +} +#else +#define displayMixLine(y, md) displayMixInfos(y, md) +#endif + +void displayExpoInfos(coord_t y, ExpoData *ed) +{ +#if defined(PCBTARANIS) + putsCurveRef(EXPO_LINE_CURVE_POS, y, ed->curve, 0); +#else + if (ed->curveMode == MODE_CURVE) + putsCurve(EXPO_LINE_EXPO_POS-3*FW, y, ed->curveParam); + else + displayGVar(EXPO_LINE_EXPO_POS, y, ed->curveParam, -100, 100); +#endif + + putsSwitches(EXPO_LINE_SWITCH_POS, y, ed->swtch, 0); +} + +#if defined(PCBHORUS) +void displayExpoLine(coord_t y, ExpoData *ed) +{ + putsMixerSource(EXPO_LINE_SRC_POS, y, ed->srcRaw, 0); + + if (ed->carryTrim != TRIM_ON) { + lcd_putc(EXPO_LINE_TRIM_POS, y, ed->carryTrim > 0 ? '-' : STR_RETA123[-ed->carryTrim]); + } + + if (!ed->flightModes || ((ed->curve.value || ed->swtch) && ((get_tmr10ms() / 200) & 1))) + displayExpoInfos(y, ed); + else + displayFlightModes(EXPO_LINE_FM_POS, y, ed->flightModes); + + if (ed->name[0]) { + lcd_putsnAtt(EXPO_LINE_NAME_POS, y, ed->name, sizeof(ed->name), ZCHAR); + } +} +#elif defined(PCBTARANIS) +void displayExpoLine(coord_t y, ExpoData *ed) +{ + putsMixerSource(EXPO_LINE_SRC_POS, y, ed->srcRaw, 0); + + if (ed->carryTrim != TRIM_ON) { + lcd_putc(EXPO_LINE_TRIM_POS, y, ed->carryTrim > 0 ? '-' : STR_RETA123[-ed->carryTrim]); + } + + if (!ed->flightModes || ((ed->curve.value || ed->swtch) && ((get_tmr10ms() / 200) & 1))) + displayExpoInfos(y, ed); + else + displayFlightModes(EXPO_LINE_FM_POS, y, ed->flightModes); + + if (ed->name[0]) { + lcd_putsnAtt(EXPO_LINE_NAME_POS, y, ed->name, sizeof(ed->name), ZCHAR); + } +} +#elif defined(CPUARM) +void displayExpoLine(coord_t y, ExpoData *ed) +{ + displayExpoInfos(y, ed); + + if (ed->name[0]) { + lcd_putsnAtt(EXPO_LINE_NAME_POS, y, ed->name, sizeof(ed->name), ZCHAR); + } +} +#else +#define displayExpoLine(y, ed) \ + displayExpoInfos(y, ed); \ + displayFlightModes(EXPO_LINE_FM_POS, y, ed->flightModes) +#endif + +void menuModelExpoMix(uint8_t expo, uint8_t event) +{ + uint8_t sub = m_posVert; + + if (s_editMode > 0) + s_editMode = 0; + + uint8_t chn = (expo ? expoAddress(s_currIdx)->chn+1 : mixAddress(s_currIdx)->destCh+1); + + switch (event) + { + case EVT_ENTRY: + case EVT_ENTRY_UP: + s_copyMode = 0; + s_copyTgtOfs = 0; + break; + case EVT_KEY_LONG(KEY_EXIT): + if (s_copyMode && s_copyTgtOfs == 0) { + deleteExpoMix(expo, s_currIdx); + killEvents(event); + event = 0; + } + // no break +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_LONG: + if (s_copyMode) { + killEvents(event); + } +#endif + case EVT_KEY_BREAK(KEY_EXIT): + if (s_copyMode) { + if (s_copyTgtOfs) { + // cancel the current copy / move operation + if (s_copyMode == COPY_MODE) { + deleteExpoMix(expo, s_currIdx); + } + else { + do { + swapExpoMix(expo, s_currIdx, s_copyTgtOfs > 0); + s_copyTgtOfs += (s_copyTgtOfs < 0 ? +1 : -1); + } while (s_copyTgtOfs != 0); + eeDirty(EE_MODEL); + } + m_posVert = s_copySrcRow; + s_copyTgtOfs = 0; + } + s_copyMode = 0; + event = 0; + } + break; + case EVT_KEY_BREAK(KEY_ENTER): + if (sub != 0 && (!s_currCh || (s_copyMode && !s_copyTgtOfs)) && !READ_ONLY()) { + s_copyMode = (s_copyMode == COPY_MODE ? MOVE_MODE : COPY_MODE); + s_copySrcIdx = s_currIdx; + s_copySrcCh = chn; + s_copySrcRow = sub; + break; + } + // no break + + CASE_EVT_ROTARY_BREAK + case EVT_KEY_LONG(KEY_ENTER): + killEvents(event); + if (s_copyTgtOfs) { + s_copyMode = 0; + s_copyTgtOfs = 0; + } + else if (sub != 0) { + if (READ_ONLY()) { + if (!s_currCh) { + pushMenu(expo ? menuModelExpoOne : menuModelMixOne); + } + } + else { + if (s_copyMode) s_currCh = 0; +#if defined(NAVIGATION_MENUS) + if (s_currCh) { + if (reachExpoMixCountLimit(expo)) break; + insertExpoMix(expo, s_currIdx); + pushMenu(expo ? menuModelExpoOne : menuModelMixOne); + s_copyMode = 0; + } + else { + event = 0; + s_copyMode = 0; + MENU_ADD_ITEM(STR_EDIT); + MENU_ADD_ITEM(STR_INSERT_BEFORE); + MENU_ADD_ITEM(STR_INSERT_AFTER); + MENU_ADD_ITEM(STR_COPY); + MENU_ADD_ITEM(STR_MOVE); + MENU_ADD_ITEM(STR_DELETE); + menuHandler = onExpoMixMenu; + } +#else + if (s_currCh) { + if (reachExpoMixCountLimit(expo)) break; + insertExpoMix(expo, s_currIdx); + } + pushMenu(expo ? menuModelExpoOne : menuModelMixOne); + s_copyMode = 0; +#endif + } + } + break; + case EVT_KEY_LONG(KEY_LEFT): + case EVT_KEY_LONG(KEY_RIGHT): + if (s_copyMode && !s_copyTgtOfs) { + if (reachExpoMixCountLimit(expo)) break; + s_currCh = chn; + if (event == EVT_KEY_LONG(KEY_RIGHT)) { s_currIdx++; m_posVert++; } + insertExpoMix(expo, s_currIdx); + pushMenu(expo ? menuModelExpoOne : menuModelMixOne); + s_copyMode = 0; + killEvents(event); + } + break; +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_LEFT: + case EVT_ROTARY_RIGHT: +#endif + case EVT_KEY_FIRST(KEY_MOVE_UP): + case EVT_KEY_REPT(KEY_MOVE_UP): + case EVT_KEY_FIRST(KEY_MOVE_DOWN): + case EVT_KEY_REPT(KEY_MOVE_DOWN): + if (s_copyMode) { + uint8_t key = (event & 0x1f); + uint8_t next_ofs = ((IS_ROTARY_UP(event) || key==KEY_MOVE_UP) ? s_copyTgtOfs - 1 : s_copyTgtOfs + 1); + + if (s_copyTgtOfs==0 && s_copyMode==COPY_MODE) { + // insert a mix on the same channel (just above / just below) + if (reachExpoMixCountLimit(expo)) break; + copyExpoMix(expo, s_currIdx); + if (IS_ROTARY_DOWN(event) || key==KEY_MOVE_DOWN) s_currIdx++; + else if (sub-s_pgOfs >= 6) s_pgOfs++; + } + else if (next_ofs==0 && s_copyMode==COPY_MODE) { + // delete the mix + deleteExpoMix(expo, s_currIdx); + if (IS_ROTARY_UP(event) || key==KEY_MOVE_UP) s_currIdx--; + } + else { + // only swap the mix with its neighbor + if (!swapExpoMix(expo, s_currIdx, IS_ROTARY_UP(event) || key==KEY_MOVE_UP)) break; + eeDirty(EE_MODEL); + } + + s_copyTgtOfs = next_ofs; + } + break; + } + +#if !defined(PCBHORUS) + lcd_outdezAtt(FW*max(sizeof(TR_MENUINPUTS), sizeof(TR_MIXER))+FW+FW/2, 0, getExpoMixCount(expo)); + lcd_puts(FW*max(sizeof(TR_MENUINPUTS), sizeof(TR_MIXER))+FW+FW/2, 0, expo ? STR_MAX(MAX_EXPOS) : STR_MAX(MAX_MIXERS)); +#endif + + SIMPLE_MENU(expo ? STR_MENUINPUTS : STR_MIXER, menuTabModel, expo ? e_InputsAll : e_MixAll, s_maxLines); + +#if defined(PCBHORUS) + lcd_outdezAtt(15+10*max(sizeof(TR_MENUINPUTS), sizeof(TR_MIXER)), 6, getExpoMixCount(expo), MIDSIZE|WHITE); + lcd_putsAtt(15+10*max(sizeof(TR_MENUINPUTS), sizeof(TR_MIXER)), 6, expo ? STR_MAX(MAX_EXPOS) : STR_MAX(MAX_MIXERS), MIDSIZE|WHITE); +#endif + + sub = m_posVert; + s_currCh = 0; + uint8_t cur = 1; + uint8_t i = 0; + + for (uint8_t ch=1; ch<=(expo ? NUM_INPUTS : NUM_CHNOUT); ch++) { + void *pointer = NULL; MixData * &md = (MixData * &)pointer; ExpoData * &ed = (ExpoData * &)pointer; + coord_t y = MENU_TITLE_HEIGHT-FH+1+(cur-s_pgOfs)*FH; + if (expo ? (ichn+1 == ch && EXPO_VALID(ed)) : (isrcRaw && md->destCh+1 == ch)) { + if (s_pgOfs < cur && cur-s_pgOfs < LCD_LINES) { + if (expo) { +#if defined(PCBTARANIS) + putsMixerSource(0, y, ch, 0); +#else + putsMixerSource(0, y, MIXSRC_Rud+ch-1, 0); +#endif + } + else { + putsChn(0, y, ch, 0); // show CHx + } + } + uint8_t mixCnt = 0; + do { + if (s_copyMode) { + if (s_copyMode == MOVE_MODE && s_pgOfs < cur && cur-s_pgOfs < 8 && s_copySrcCh == ch && s_copyTgtOfs != 0 && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { + lcd_rect(expo ? 18 : 22, y-1, expo ? LCD_W-18 : LCD_W-22, 9, DOTTED); + cur++; y+=FH; + } + if (s_currIdx == i) { + sub = m_posVert = cur; + s_currCh = ch; + } + } + else if (sub == cur) { + s_currIdx = i; + } + if (s_pgOfs < cur && cur-s_pgOfs < 8) { + uint8_t attr = ((s_copyMode || sub != cur) ? 0 : INVERS); + if (expo) { + ed->weight = GVAR_MENU_ITEM(EXPO_LINE_WEIGHT_POS, y, ed->weight, MIN_EXPO_WEIGHT, 100, attr | (isExpoActive(i) ? BOLD : 0), 0, event); + displayExpoLine(y, ed); + if (ed->mode!=3) { + lcd_putc(EXPO_LINE_SIDE_POS, y, ed->mode == 2 ? 126 : 127); + } + } + else { +#if LCD_W >= 212 + if (attr) { + displayHeaderChannelName(ch); + } +#endif + + if (mixCnt > 0) lcd_putsiAtt(FW, y, STR_VMLTPX2, md->mltpx, 0); + + putsMixerSource(MIX_LINE_SRC_POS, y, md->srcRaw, 0); + + gvarWeightItem(MIX_LINE_WEIGHT_POS, y, md, attr | (isMixActive(i) ? BOLD : 0), event); + + displayMixLine(y, md); + + char cs = ' '; + if (md->speedDown || md->speedUp) + cs = 'S'; + if (md->delayUp || md->delayDown) + cs = (cs =='S' ? '*' : 'D'); + lcd_putc(MIX_LINE_DELAY_POS, y, cs); + } + if (s_copyMode) { + if ((s_copyMode==COPY_MODE || s_copyTgtOfs == 0) && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { + /* draw a border around the raw on selection mode (copy/move) */ + lcd_rect(expo ? EXPO_LINE_SELECT_POS : 22, y-1, expo ? (LCD_W-EXPO_LINE_SELECT_POS) : (LCD_W-22), 9, s_copyMode == COPY_MODE ? SOLID : DOTTED); + } + if (cur == sub) { + /* invert the raw when it's the current one */ + lcd_filled_rect(expo ? EXPO_LINE_SELECT_POS+1 : 23, y, expo ? (LCD_W-EXPO_LINE_SELECT_POS-2) : (LCD_W-24), 7); + } + } + } + cur++; y+=FH; mixCnt++; i++; if (expo) ed++; else md++; + } while (expo ? (ichn+1 == ch && EXPO_VALID(ed)) : (isrcRaw && md->destCh+1 == ch)); + if (s_copyMode == MOVE_MODE && s_pgOfs < cur && cur-s_pgOfs < LCD_LINES && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { + lcd_rect(expo ? EXPO_LINE_SELECT_POS : 22, y-1, expo ? LCD_W-EXPO_LINE_SELECT_POS : LCD_W-22, 9, DOTTED); + cur++; y+=FH; + } + } + else { + uint8_t attr = 0; + if (sub == cur) { + s_currIdx = i; + s_currCh = ch; + if (!s_copyMode) { + attr = INVERS; + } + } + if (s_pgOfs < cur && cur-s_pgOfs < LCD_LINES) { + if (expo) { +#if defined(PCBTARANIS) + putsMixerSource(0, y, ch, attr); +#else + putsMixerSource(0, y, MIXSRC_Rud+ch-1, attr); +#endif + } + else { + putsChn(0, y, ch, attr); // show CHx +#if LCD_W >= 212 + if (attr) { + displayHeaderChannelName(ch); + } +#endif + } + if (s_copyMode == MOVE_MODE && s_copySrcCh == ch) { + lcd_rect(expo ? EXPO_LINE_SELECT_POS : 22, y-1, expo ? (LCD_W-EXPO_LINE_SELECT_POS) : (LCD_W-22), 9, DOTTED); + } + } + cur++; y+=FH; + } + } + s_maxLines = cur; + if (sub >= s_maxLines-1) m_posVert = s_maxLines-1; +} + +void menuModelExposAll(uint8_t event) +{ + return menuModelExpoMix(1, event); +} + +void menuModelMixAll(uint8_t event) +{ + return menuModelExpoMix(0, event); +} diff --git a/radio/src/gui/menu_model_select.cpp b/radio/src/gui/menu_model_select.cpp new file mode 100755 index 000000000..aae55068c --- /dev/null +++ b/radio/src/gui/menu_model_select.cpp @@ -0,0 +1,591 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "../opentx.h" + +uint8_t eeFindEmptyModel(uint8_t id, bool down) +{ + uint8_t i = id; + for (;;) { + i = (MAX_MODELS + (down ? i+1 : i-1)) % MAX_MODELS; + if (!eeModelExists(i)) break; + if (i == id) return 0xff; // no free space in directory left + } + return i; +} + +void selectModel(uint8_t sub) +{ + displayPopup(STR_LOADINGMODEL); + saveTimers(); + eeCheck(true); // force writing of current model data before this is changed + g_eeGeneral.currModel = sub; + eeDirty(EE_GENERAL); + eeLoadModel(sub); +} + +#if defined(SDCARD) +#define LIST_NONE_SD_FILE 1 +bool listSdFiles(const char *path, const char *extension, const uint8_t maxlen, const char *selection, uint8_t flags=0) +{ + FILINFO fno; + DIR dir; + char *fn; /* This function is assuming non-Unicode cfg. */ +#if _USE_LFN + TCHAR lfn[_MAX_LFN + 1]; + fno.lfname = lfn; + fno.lfsize = sizeof(lfn); +#endif + + static uint16_t s_last_menu_offset = 0; + +#if defined(CPUARM) + static uint8_t s_last_flags; + + if (selection) { + s_last_flags = flags; + memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); + strcpy(reusableBuffer.modelsel.menu_bss[0], path); + strcat(reusableBuffer.modelsel.menu_bss[0], "/"); + strncat(reusableBuffer.modelsel.menu_bss[0], selection, maxlen); + strcat(reusableBuffer.modelsel.menu_bss[0], extension); + if (f_stat(reusableBuffer.modelsel.menu_bss[0], &fno) != FR_OK) { + selection = NULL; + } + } + else { + flags = s_last_flags; + } +#endif + + if (s_menu_offset == 0) { + s_last_menu_offset = 0; + memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); + } + else if (s_menu_offset == s_menu_count - MENU_MAX_LINES) { + s_last_menu_offset = 0xffff; + memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); + } + else if (s_menu_offset == s_last_menu_offset) { + // should not happen, only there because of Murphy's law + return true; + } + else if (s_menu_offset > s_last_menu_offset) { + memmove(reusableBuffer.modelsel.menu_bss[0], reusableBuffer.modelsel.menu_bss[1], (MENU_MAX_LINES-1)*MENU_LINE_LENGTH); + memset(reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-1], 0xff, MENU_LINE_LENGTH); + } + else { + memmove(reusableBuffer.modelsel.menu_bss[1], reusableBuffer.modelsel.menu_bss[0], (MENU_MAX_LINES-1)*MENU_LINE_LENGTH); + memset(reusableBuffer.modelsel.menu_bss[0], 0, MENU_LINE_LENGTH); + } + + s_menu_count = 0; + s_menu_flags = BSS; + + FRESULT res = f_opendir(&dir, path); /* Open the directory */ + if (res == FR_OK) { + + if (flags & LIST_NONE_SD_FILE) { + s_menu_count++; + if (selection) { + s_last_menu_offset++; + } + else if (s_menu_offset==0 || s_menu_offset < s_last_menu_offset) { + char *line = reusableBuffer.modelsel.menu_bss[0]; + memset(line, 0, MENU_LINE_LENGTH); + strcpy(line, "---"); + s_menu[0] = line; + } + } + + for (;;) { + res = f_readdir(&dir, &fno); /* Read a directory item */ + if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ + +#if _USE_LFN + fn = *fno.lfname ? fno.lfname : fno.fname; +#else + fn = fno.fname; +#endif + + uint8_t len = strlen(fn); + if (len < 5 || len > maxlen+4 || strcasecmp(fn+len-4, extension) || (fno.fattrib & AM_DIR)) continue; + + s_menu_count++; + fn[len-4] = '\0'; + + if (s_menu_offset == 0) { + if (selection && strncasecmp(fn, selection, maxlen) < 0) { + s_last_menu_offset++; + } + else { + for (uint8_t i=0; i=0; i--) { + char *line = reusableBuffer.modelsel.menu_bss[i]; + if (line[0] == '\0' || strcasecmp(fn, line) > 0) { + if (i > 0) memmove(reusableBuffer.modelsel.menu_bss[0], reusableBuffer.modelsel.menu_bss[1], sizeof(reusableBuffer.modelsel.menu_bss[i]) * i); + memset(line, 0, MENU_LINE_LENGTH); + strcpy(line, fn); + break; + } + } + for (uint8_t i=0; i s_last_menu_offset) { + if (strcasecmp(fn, reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-2]) > 0 && strcasecmp(fn, reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-1]) < 0) { + memset(reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-1], 0, MENU_LINE_LENGTH); + strcpy(reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-1], fn); + } + } + else { + if (strcasecmp(fn, reusableBuffer.modelsel.menu_bss[1]) < 0 && strcasecmp(fn, reusableBuffer.modelsel.menu_bss[0]) > 0) { + memset(reusableBuffer.modelsel.menu_bss[0], 0, MENU_LINE_LENGTH); + strcpy(reusableBuffer.modelsel.menu_bss[0], fn); + } + } + } + } + + if (s_menu_offset > 0) + s_last_menu_offset = s_menu_offset; + else + s_menu_offset = s_last_menu_offset; + + return s_menu_count; +} +#endif + +#if defined(PCBTARANIS) + static int8_t modelselBitmapIdx; + static uint8_t modelselBitmap[MODEL_BITMAP_SIZE]; + #define MODELSEL_W 133 + #define BMP_DIRTY() modelselBitmapIdx = -1 +#else + #define MODELSEL_W LCD_W + #define BMP_DIRTY() +#endif + +#if defined(NAVIGATION_MENUS) +void onModelSelectMenu(const char *result) +{ + int8_t sub = m_posVert; + + if (result == STR_SELECT_MODEL || result == STR_CREATE_MODEL) { + selectModel(sub); + } + else if (result == STR_COPY_MODEL) { + s_copyMode = COPY_MODE; + s_copyTgtOfs = 0; + s_copySrcRow = -1; + } + else if (result == STR_MOVE_MODEL) { + s_copyMode = MOVE_MODE; + s_copyTgtOfs = 0; + s_copySrcRow = -1; + } +#if defined(SDCARD) + else if (result == STR_BACKUP_MODEL) { + eeCheck(true); // force writing of current model data before this is changed + POPUP_WARNING(eeBackupModel(sub)); + } + else if (result == STR_RESTORE_MODEL || result == STR_UPDATE_LIST) { + if (!listSdFiles(MODELS_PATH, MODELS_EXT, MENU_LINE_LENGTH-1, NULL)) { + POPUP_WARNING(STR_NO_MODELS_ON_SD); + s_menu_flags = 0; + } + } +#endif + else if (result == STR_DELETE_MODEL) { + POPUP_CONFIRMATION(STR_DELETEMODEL); +#if defined(CPUARM) + SET_WARNING_INFO(modelHeaders[sub].name, sizeof(g_model.header.name), ZCHAR); +#else + char * name = reusableBuffer.modelsel.mainname; + eeLoadModelName(sub, name); + SET_WARNING_INFO(name, sizeof(g_model.header.name), ZCHAR); +#endif + } +#if defined(SDCARD) + else { + // The user choosed a file on SD to restore + POPUP_WARNING(eeRestoreModel(sub, (char *)result)); + BMP_DIRTY(); + if (!s_warning && g_eeGeneral.currModel == sub) + eeLoadModel(sub); + } +#endif +} +#endif + +void menuModelSelect(uint8_t event) +{ + if (s_warning_result) { + s_warning_result = 0; + eeDeleteModel(m_posVert); // delete file + s_copyMode = 0; + event = EVT_ENTRY_UP; + BMP_DIRTY(); + } + + uint8_t _event_ = (IS_ROTARY_BREAK(event) || IS_ROTARY_LONG(event) ? 0 : event); + +#if defined(PCBTARANIS) + if ((s_copyMode && EVT_KEY_MASK(event) == KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) + _event_ -= KEY_EXIT; +#else + if (s_copyMode && EVT_KEY_MASK(event) == KEY_EXIT) + _event_ -= KEY_EXIT; +#endif + + int8_t oldSub = m_posVert; + + check_submenu_simple(_event_, MAX_MODELS-1); + +#if defined(NAVIGATION_POT2) + if (event==0 && p2valdiff<0) { + event = EVT_KEY_FIRST(KEY_RIGHT); + } +#endif + + if (s_editMode > 0) s_editMode = 0; + +#if !defined(CPUARM) + if (event) { + eeFlush(); // flush eeprom write + } +#endif + + int8_t sub = m_posVert; + + switch (event) + { + case EVT_ENTRY: + m_posVert = sub = g_eeGeneral.currModel; + if (sub >= LCD_LINES-1) s_pgOfs = sub-LCD_LINES+2; + s_copyMode = 0; + s_editMode = EDIT_MODE_INIT; + BMP_DIRTY(); + eeCheck(true); + break; + case EVT_KEY_LONG(KEY_EXIT): + if (s_copyMode && s_copyTgtOfs == 0 && g_eeGeneral.currModel != sub && eeModelExists(sub)) { + POPUP_CONFIRMATION(STR_DELETEMODEL); +#if defined(CPUARM) + SET_WARNING_INFO(modelHeaders[sub].name, sizeof(g_model.header.name), ZCHAR); +#else + char * name = reusableBuffer.modelsel.mainname; + eeLoadModelName(sub, name); + SET_WARNING_INFO(name, sizeof(g_model.header.name), ZCHAR); +#endif + killEvents(event); + break; + } + // no break +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_LONG: + killEvents(event); + if (s_editMode < 0) { + popMenu(); + break; + } + else if (!s_copyMode) { + m_posVert = sub = g_eeGeneral.currModel; + s_copyMode = 0; + s_editMode = EDIT_MODE_INIT; + } + // no break +#endif + case EVT_KEY_BREAK(KEY_EXIT): + if (s_copyMode) { + sub = m_posVert = (s_copyMode == MOVE_MODE || s_copySrcRow<0) ? (MAX_MODELS+sub+s_copyTgtOfs) % MAX_MODELS : s_copySrcRow; + s_copyMode = 0; + } +#if defined(PCBTARANIS) + else { + if (m_posVert != g_eeGeneral.currModel) { + m_posVert = g_eeGeneral.currModel; + s_pgOfs = 0; + } + else if (event != EVT_KEY_LONG(KEY_EXIT)) { + popMenu(); + } + } +#endif + break; +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_BREAK: + if (s_editMode == -1) { + s_editMode = 0; + break; + } + // no break; +#endif + case EVT_KEY_LONG(KEY_ENTER): + case EVT_KEY_BREAK(KEY_ENTER): + s_editMode = 0; + if (READ_ONLY()) { + if (g_eeGeneral.currModel != sub && eeModelExists(sub)) { + selectModel(sub); + } + } + else if (s_copyMode && (s_copyTgtOfs || s_copySrcRow>=0)) { + displayPopup(s_copyMode==COPY_MODE ? STR_COPYINGMODEL : STR_MOVINGMODEL); + eeCheck(true); // force writing of current model data before this is changed + + uint8_t cur = (MAX_MODELS + sub + s_copyTgtOfs) % MAX_MODELS; + + if (s_copyMode == COPY_MODE) { + if (eeCopyModel(cur, s_copySrcRow)) + BMP_DIRTY(); + else + cur = sub; + } + + s_copySrcRow = g_eeGeneral.currModel; // to update the currModel value + while (sub != cur) { + uint8_t src = cur; + cur = (s_copyTgtOfs > 0 ? cur+MAX_MODELS-1 : cur+1) % MAX_MODELS; + eeSwapModels(src, cur); + BMP_DIRTY(); + if (src == s_copySrcRow) + s_copySrcRow = cur; + else if (cur == s_copySrcRow) + s_copySrcRow = src; + } + + if (s_copySrcRow != g_eeGeneral.currModel) { + g_eeGeneral.currModel = s_copySrcRow; + eeDirty(EE_GENERAL); + } + + s_copyMode = 0; + event = EVT_ENTRY_UP; + } + else if (event == EVT_KEY_LONG(KEY_ENTER) +#if !defined(PCBTARANIS) + || IS_ROTARY_BREAK(event) +#endif + ) { + s_copyMode = 0; + killEvents(event); +#if defined(NAVIGATION_MENUS) + if (g_eeGeneral.currModel != sub) { + if (eeModelExists(sub)) { + MENU_ADD_ITEM(STR_SELECT_MODEL); + MENU_ADD_SD_ITEM(STR_BACKUP_MODEL); + MENU_ADD_ITEM(STR_COPY_MODEL); + MENU_ADD_ITEM(STR_MOVE_MODEL); + MENU_ADD_ITEM(STR_DELETE_MODEL); + } + else { +#if defined(SDCARD) + MENU_ADD_ITEM(STR_CREATE_MODEL); + MENU_ADD_ITEM(STR_RESTORE_MODEL); +#else + selectModel(sub); +#endif + } + } + else { + MENU_ADD_SD_ITEM(STR_BACKUP_MODEL); + MENU_ADD_ITEM(STR_COPY_MODEL); + MENU_ADD_ITEM(STR_MOVE_MODEL); + } + menuHandler = onModelSelectMenu; +#else + if (g_eeGeneral.currModel != sub) { + selectModel(sub); + } +#endif + } + else if (eeModelExists(sub)) { + s_copyMode = (s_copyMode == COPY_MODE ? MOVE_MODE : COPY_MODE); + s_copyTgtOfs = 0; + s_copySrcRow = -1; + } + break; + +#if defined(PCBTARANIS) + case EVT_KEY_BREAK(KEY_PAGE): + case EVT_KEY_LONG(KEY_PAGE): + chainMenu(event == EVT_KEY_BREAK(KEY_PAGE) ? menuModelSetup : menuTabModel[DIM(menuTabModel)-1]); + killEvents(event); + break; +#else +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_LEFT: + case EVT_ROTARY_RIGHT: +#endif + case EVT_KEY_FIRST(KEY_LEFT): + case EVT_KEY_FIRST(KEY_RIGHT): +#if defined(ROTARY_ENCODER_NAVIGATION) + if ((!IS_ROTARY_RIGHT(event) && !IS_ROTARY_LEFT(event)) || s_editMode < 0) { +#endif + if (sub == g_eeGeneral.currModel) { + chainMenu((IS_ROTARY_RIGHT(event) || event == EVT_KEY_FIRST(KEY_RIGHT)) ? menuModelSetup : menuTabModel[DIM(menuTabModel)-1]); + } + else { + AUDIO_WARNING2(); + } + break; +#if defined(ROTARY_ENCODER_NAVIGATION) + } + // no break +#endif +#endif + + case EVT_KEY_FIRST(KEY_MOVE_UP): + case EVT_KEY_REPT(KEY_MOVE_UP): + case EVT_KEY_FIRST(KEY_MOVE_DOWN): + case EVT_KEY_REPT(KEY_MOVE_DOWN): + if (s_copyMode) { + int8_t next_ofs = s_copyTgtOfs + oldSub - m_posVert; + if (next_ofs == MAX_MODELS || next_ofs == -MAX_MODELS) + next_ofs = 0; + + if (s_copySrcRow < 0 && s_copyMode==COPY_MODE) { + s_copySrcRow = oldSub; + // find a hole (in the first empty slot above / below) + sub = eeFindEmptyModel(s_copySrcRow, IS_ROTARY_DOWN(event) || event==EVT_KEY_FIRST(KEY_MOVE_DOWN)); + if (sub < 0) { + // no free room for duplicating the model + AUDIO_ERROR(); + sub = oldSub; + s_copyMode = 0; + } + next_ofs = 0; + m_posVert = sub; + } + s_copyTgtOfs = next_ofs; + } + break; + } + +#if defined(PCBHORUS) +#elif defined(PCBTARANIS) + lcd_puts(27*FW-(LEN_FREE-4)*FW, 0, STR_FREE); + if (event) reusableBuffer.modelsel.eepromfree = EeFsGetFree(); + lcd_outdezAtt(20*FW, 0, reusableBuffer.modelsel.eepromfree, 0); + lcd_puts(21*FW, 0, STR_BYTES); +#elif !defined(PCBSKY9X) + lcd_puts(9*FW-(LEN_FREE-4)*FW, 0, STR_FREE); + if (event) reusableBuffer.modelsel.eepromfree = EeFsGetFree(); + lcd_outdezAtt(17*FW, 0, reusableBuffer.modelsel.eepromfree, 0); +#endif + +#if defined(PCBHORUS) + // nothing +#elif defined(PCBTARANIS) + displayScreenIndex(e_ModelSelect, DIM(menuTabModel), 0); + lcd_filled_rect(0, 0, LCD_W, FH, SOLID, FILL_WHITE|GREY_DEFAULT); +#elif defined(ROTARY_ENCODER_NAVIGATION) + displayScreenIndex(e_ModelSelect, DIM(menuTabModel), (sub == g_eeGeneral.currModel) ? ((IS_RE_NAVIGATION_ENABLE() && s_editMode < 0) ? INVERS|BLINK : INVERS) : 0); +#else + displayScreenIndex(e_ModelSelect, DIM(menuTabModel), (sub == g_eeGeneral.currModel) ? INVERS : 0); +#endif + + TITLE(STR_MENUMODELSEL); + + for (uint8_t i=0; i= 0)) { + if (k == sub) { + if (s_copyMode == COPY_MODE) { + k = s_copySrcRow; + lcd_putc(MODELSEL_W-FW, y, '+'); + } + else { + k = sub + s_copyTgtOfs; + } + } + else if (s_copyTgtOfs < 0 && ((k < sub && k >= sub+s_copyTgtOfs) || (k-MAX_MODELS < sub && k-MAX_MODELS >= sub+s_copyTgtOfs))) + k += 1; + else if (s_copyTgtOfs > 0 && ((k > sub && k <= sub+s_copyTgtOfs) || (k+MAX_MODELS > sub && k+MAX_MODELS <= sub+s_copyTgtOfs))) + k += MAX_MODELS-1; + } + + k %= MAX_MODELS; + + if (eeModelExists(k)) { +#if defined(PCBSKY9X) + putsModelName(4*FW, y, modelHeaders[k].name, k, 0); +#elif defined(CPUARM) + putsModelName(4*FW, y, modelHeaders[k].name, k, 0); + lcd_outdezAtt(20*FW, y, eeModelSize(k), 0); +#else + char * name = reusableBuffer.modelsel.listnames[i]; + if (event) eeLoadModelName(k, name); + putsModelName(4*FW, y, name, k, 0); + lcd_outdezAtt(20*FW, y, eeModelSize(k), 0); +#endif + if (k==g_eeGeneral.currModel && (s_copyMode!=COPY_MODE || s_copySrcRow<0 || i+s_pgOfs!=(vertpos_t)sub)) lcd_putc(1, y, '*'); + } + + if (s_copyMode && (vertpos_t)sub==i+s_pgOfs) { + lcd_filled_rect(9, y, MODELSEL_W-1-9, 7); + lcd_rect(8, y-1, MODELSEL_W-1-7, 9, s_copyMode == COPY_MODE ? SOLID : DOTTED); + } + } + +#if defined(PCBTARANIS) + if (modelselBitmapIdx != m_posVert) { + modelselBitmapIdx = m_posVert; + if (modelselBitmapIdx == g_eeGeneral.currModel) + memcpy(modelselBitmap, modelBitmap, MODEL_BITMAP_SIZE); + else + loadModelBitmap(modelHeaders[sub].bitmap, modelselBitmap); + } +#if !defined(PCBHORUS) + lcd_bmp(22*FW+2, 2*FH+FH/2, modelselBitmap); +#endif +#endif +} diff --git a/radio/src/gui/menu_model_setup.cpp b/radio/src/gui/menu_model_setup.cpp new file mode 100755 index 000000000..c57a0c2ab --- /dev/null +++ b/radio/src/gui/menu_model_setup.cpp @@ -0,0 +1,1129 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "../opentx.h" + +#if defined(CPUARM) +uint8_t g_moduleIdx; +void menuModelFailsafe(uint8_t event); +#endif + +enum menuModelSetupItems { + ITEM_MODEL_NAME, + CASE_PCBTARANIS(ITEM_MODEL_BITMAP) + ITEM_MODEL_TIMER1, + CASE_CPUARM(ITEM_MODEL_TIMER1_NAME) + CASE_PERSISTENT_TIMERS(ITEM_MODEL_TIMER1_PERSISTENT) + ITEM_MODEL_TIMER1_MINUTE_BEEP, + ITEM_MODEL_TIMER1_COUNTDOWN_BEEP, + ITEM_MODEL_TIMER2, + CASE_CPUARM(ITEM_MODEL_TIMER2_NAME) + CASE_PERSISTENT_TIMERS(ITEM_MODEL_TIMER2_PERSISTENT) + ITEM_MODEL_TIMER2_MINUTE_BEEP, + ITEM_MODEL_TIMER2_COUNTDOWN_BEEP, + CASE_CPUARM(ITEM_MODEL_TIMER3) + CASE_CPUARM(ITEM_MODEL_TIMER3_NAME) + CASE_CPUARM(ITEM_MODEL_TIMER3_PERSISTENT) + CASE_CPUARM(ITEM_MODEL_TIMER3_MINUTE_BEEP) + CASE_CPUARM(ITEM_MODEL_TIMER3_COUNTDOWN_BEEP) + ITEM_MODEL_EXTENDED_LIMITS, + ITEM_MODEL_EXTENDED_TRIMS, + CASE_CPUARM(ITEM_MODEL_DISPLAY_TRIMS) + ITEM_MODEL_TRIM_INC, + CASE_PCBTARANIS(ITEM_MODEL_THROTTLE_LABEL) + ITEM_MODEL_THROTTLE_REVERSED, + ITEM_MODEL_THROTTLE_TRACE, + ITEM_MODEL_THROTTLE_TRIM, + CASE_CPUARM(ITEM_MODEL_PREFLIGHT_LABEL) + CASE_CPUARM(ITEM_MODEL_CHECKLIST_DISPLAY) + ITEM_MODEL_THROTTLE_WARNING, + ITEM_MODEL_SWITCHES_WARNING, + CASE_PCBTARANIS(ITEM_MODEL_SWITCHES_WARNING2) + CASE_PCBTARANIS(ITEM_MODEL_POT_WARNING) + ITEM_MODEL_BEEP_CENTER, + CASE_CPUARM(ITEM_MODEL_USE_GLOBAL_FUNCTIONS) +#if defined(PCBTARANIS) + ITEM_MODEL_INTERNAL_MODULE_LABEL, + ITEM_MODEL_INTERNAL_MODULE_MODE, + ITEM_MODEL_INTERNAL_MODULE_CHANNELS, + ITEM_MODEL_INTERNAL_MODULE_BIND, + ITEM_MODEL_INTERNAL_MODULE_FAILSAFE, + ITEM_MODEL_EXTERNAL_MODULE_LABEL, + ITEM_MODEL_EXTERNAL_MODULE_MODE, + ITEM_MODEL_EXTERNAL_MODULE_CHANNELS, + ITEM_MODEL_EXTERNAL_MODULE_BIND, + ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE, + ITEM_MODEL_TRAINER_LABEL, + ITEM_MODEL_TRAINER_MODE, + ITEM_MODEL_TRAINER_CHANNELS, + ITEM_MODEL_TRAINER_SETTINGS, +#elif defined(CPUARM) + ITEM_MODEL_EXTERNAL_MODULE_LABEL, + ITEM_MODEL_EXTERNAL_MODULE_MODE, + ITEM_MODEL_EXTERNAL_MODULE_CHANNELS, + ITEM_MODEL_EXTERNAL_MODULE_BIND, + ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE, +#if defined(PCBSKY9X) && !defined(REVA) && !defined(REVX) + ITEM_MODEL_EXTRA_MODULE_LABEL, + ITEM_MODEL_EXTRA_MODULE_CHANNELS, + ITEM_MODEL_EXTRA_MODULE_BIND, +#endif +#else + ITEM_MODEL_PPM1_PROTOCOL, + ITEM_MODEL_PPM1_PARAMS, +#endif + ITEM_MODEL_SETUP_MAX +}; + +#if defined(PCBSKY9X) + #define FIELD_PROTOCOL_MAX 2 +#else + #define FIELD_PROTOCOL_MAX 1 +#endif + +#if LCD_W >= 212 + #define MODEL_SETUP_2ND_COLUMN (LCD_W-17*FW-MENUS_SCROLLBAR_WIDTH) + #define MODEL_SETUP_BIND_OFS 3*FW-2 + #define MODEL_SETUP_RANGE_OFS 7*FW + #define MODEL_SETUP_SET_FAILSAFE_OFS 10*FW +#else + #define MODEL_SETUP_2ND_COLUMN (LCD_W-11*FW-MENUS_SCROLLBAR_WIDTH) + #define MODEL_SETUP_BIND_OFS 2*FW+1 + #define MODEL_SETUP_RANGE_OFS 4*FW+3 + #define MODEL_SETUP_SET_FAILSAFE_OFS 7*FW-2 +#endif + +#if defined(PCBTARANIS) && defined(SDCARD) +void copySelection(char *dst, const char *src, uint8_t size) +{ + if (memcmp(src, "---", 3) == 0) + memset(dst, 0, size); + else + memcpy(dst, src, size); +} + +void onModelSetupBitmapMenu(const char *result) +{ + if (result == STR_UPDATE_LIST) { + if (!listSdFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap), NULL)) { + POPUP_WARNING(STR_NO_BITMAPS_ON_SD); + s_menu_flags = 0; + } + } + else { + // The user choosed a bmp file in the list + copySelection(g_model.header.bitmap, result, sizeof(g_model.header.bitmap)); + LOAD_MODEL_BITMAP(); + memcpy(modelHeaders[g_eeGeneral.currModel].bitmap, g_model.header.bitmap, sizeof(g_model.header.bitmap)); + eeDirty(EE_MODEL); + } +} +#endif + +#if defined(PCBTARANIS) + #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_TRAINER_LABEL ? TRAINER_MODULE : (k>=ITEM_MODEL_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE)) +#elif defined(PCBSKY9X) && !defined(REVA) && !defined(REVX) + #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_EXTRA_MODULE_LABEL ? EXTRA_MODULE : EXTERNAL_MODULE) +#else + #define CURRENT_MODULE_EDITED(k) (EXTERNAL_MODULE) +#endif + +void menuModelSetup(uint8_t event) +{ +#if defined(PCBTARANIS) + horzpos_t l_posHorz = m_posHorz; + #define IF_INTERNAL_MODULE_ON(x) (g_model.moduleData[INTERNAL_MODULE].rfProtocol == RF_PROTO_OFF ? HIDDEN_ROW : (uint8_t)(x)) + #define IF_EXTERNAL_MODULE_ON(x) (g_model.externalModule == MODULE_TYPE_NONE ? HIDDEN_ROW : (uint8_t)(x)) + #define IF_TRAINER_ON(x) (g_model.trainerMode == TRAINER_MODE_SLAVE ? (uint8_t)(x) : HIDDEN_ROW) + #define IF_EXTERNAL_MODULE_XJT(x) (IS_MODULE_XJT(EXTERNAL_MODULE) ? (uint8_t)x : HIDDEN_ROW) + #define IS_D8_RX(x) (g_model.moduleData[x].rfProtocol == RF_PROTO_D8) + #define INTERNAL_MODULE_CHANNELS_ROWS() IF_INTERNAL_MODULE_ON(1) + #define EXTERNAL_MODULE_CHANNELS_ROWS() IF_EXTERNAL_MODULE_ON(IS_MODULE_DSM2(EXTERNAL_MODULE) ? (uint8_t)0 : (uint8_t)1) + #define TRAINER_CHANNELS_ROWS() IF_TRAINER_ON(1) + #define PORT_CHANNELS_ROWS(x) (x==INTERNAL_MODULE ? INTERNAL_MODULE_CHANNELS_ROWS() : (x==EXTERNAL_MODULE ? EXTERNAL_MODULE_CHANNELS_ROWS() : TRAINER_CHANNELS_ROWS())) + #define FAILSAFE_ROWS(x) ((g_model.moduleData[x].rfProtocol==RF_PROTO_X16 || g_model.moduleData[x].rfProtocol==RF_PROTO_LR12) ? (g_model.moduleData[x].failsafeMode==FAILSAFE_CUSTOM ? (uint8_t)1 : (uint8_t)0) : HIDDEN_ROW) + #define MODEL_SETUP_MAX_LINES (1+ITEM_MODEL_SETUP_MAX) + #define POT_WARN_ITEMS() ((g_model.nPotsToWarn >> 6) ? (uint8_t)NUM_POTS : (uint8_t)0) + #define TIMER_ROWS 2, 0, CASE_PERSISTENT_TIMERS(0) 0, 0 + bool CURSOR_ON_CELL = (m_posHorz >= 0); + MENU_TAB({ 0, 0, CASE_PCBTARANIS(0) TIMER_ROWS, TIMER_ROWS, TIMER_ROWS, 0, 1, 0, 0, CASE_PCBTARANIS(LABEL(Throttle)) 0, 0, 0, CASE_CPUARM(LABEL(PreflightCheck)) CASE_CPUARM(0) 0, uint8_t(NAVIGATION_LINE_BY_LINE|getSwitchWarningsAllowed()), ONE_2x2POS_DEFINED() ? TITLE_ROW : HIDDEN_ROW, POT_WARN_ITEMS(), NAVIGATION_LINE_BY_LINE|(NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS-1), 0, LABEL(InternalModule), 0, IF_INTERNAL_MODULE_ON(1), IF_INTERNAL_MODULE_ON(IS_D8_RX(0) ? (uint8_t)1 : (uint8_t)2), IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), LABEL(ExternalModule), (IS_MODULE_XJT(EXTERNAL_MODULE) || IS_MODULE_DSM2(EXTERNAL_MODULE)) ? (uint8_t)1 : (uint8_t)0, EXTERNAL_MODULE_CHANNELS_ROWS(), (IS_MODULE_XJT(EXTERNAL_MODULE) && IS_D8_RX(EXTERNAL_MODULE)) ? (uint8_t)1 : (IS_MODULE_PPM(EXTERNAL_MODULE) || IS_MODULE_XJT(EXTERNAL_MODULE) || IS_MODULE_DSM2(EXTERNAL_MODULE)) ? (uint8_t)2 : HIDDEN_ROW, IF_EXTERNAL_MODULE_XJT(FAILSAFE_ROWS(EXTERNAL_MODULE)), LABEL(Trainer), 0, TRAINER_CHANNELS_ROWS(), IF_TRAINER_ON(2)}); +#elif defined(CPUARM) + #define IF_EXTERNAL_MODULE_XJT(x) (IS_MODULE_XJT(EXTERNAL_MODULE) ? (uint8_t)x : HIDDEN_ROW) + #define IF_EXTERNAL_MODULE_ON(x) (g_model.externalModule == MODULE_TYPE_NONE ? HIDDEN_ROW : (uint8_t)(x)) + #define IS_D8_RX(x) (g_model.moduleData[x].rfProtocol == RF_PROTO_D8) + #define EXTERNAL_MODULE_CHANNELS_ROWS() IF_EXTERNAL_MODULE_ON(IS_MODULE_DSM2(EXTERNAL_MODULE) ? (uint8_t)0 : (uint8_t)1) + #define TRAINER_CHANNELS_ROWS() (HIDDEN_ROW) + #define PORT_CHANNELS_ROWS(x) (x==EXTERNAL_MODULE ? EXTERNAL_MODULE_CHANNELS_ROWS() : 0) + #define FAILSAFE_ROWS(x) ((g_model.moduleData[x].rfProtocol==RF_PROTO_X16 || g_model.moduleData[x].rfProtocol==RF_PROTO_LR12) ? (g_model.moduleData[x].failsafeMode==FAILSAFE_CUSTOM ? (uint8_t)1 : (uint8_t)0) : HIDDEN_ROW) + #define CURSOR_ON_CELL (true) + #define MODEL_SETUP_MAX_LINES (1+ITEM_MODEL_SETUP_MAX) + #define POT_WARN_ITEMS() ((g_model.nPotsToWarn >> 6) ? (uint8_t)NUM_POTS : (uint8_t)0) + #define TIMER_ROWS 2, 0, CASE_PERSISTENT_TIMERS(0) 0, 0 +#if (defined(PCBSKY9X) && !defined(REVA) && !defined(REVX)) + #define EXTRA_MODULE_ROWS LABEL(ExtraModule), 1, 2, +#else + #define EXTRA_MODULE_ROWS +#endif + #define TRAINER_MODULE_ROWS + MENU_TAB({ 0, 0, TIMER_ROWS, TIMER_ROWS, TIMER_ROWS, 0, 1, 0, 0, CASE_PCBTARANIS(LABEL(Throttle)) 0, 0, 0, CASE_CPUARM(LABEL(PreflightCheck)) CASE_CPUARM(0) 0, 6, NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS-1, 0, LABEL(ExternalModule), (IS_MODULE_XJT(EXTERNAL_MODULE) || IS_MODULE_DSM2(EXTERNAL_MODULE)) ? (uint8_t)1 : (uint8_t)0, EXTERNAL_MODULE_CHANNELS_ROWS(), (IS_MODULE_XJT(EXTERNAL_MODULE) && IS_D8_RX(EXTERNAL_MODULE)) ? (uint8_t)1 : (IS_MODULE_PPM(EXTERNAL_MODULE) || IS_MODULE_XJT(EXTERNAL_MODULE) || IS_MODULE_DSM2(EXTERNAL_MODULE)) ? (uint8_t)2 : HIDDEN_ROW, IF_EXTERNAL_MODULE_XJT(FAILSAFE_ROWS(EXTERNAL_MODULE)), EXTRA_MODULE_ROWS TRAINER_MODULE_ROWS }); +#elif defined(CPUM64) + #define CURSOR_ON_CELL (true) + #define MODEL_SETUP_MAX_LINES ((IS_PPM_PROTOCOL(protocol)||IS_DSM2_PROTOCOL(protocol)||IS_PXX_PROTOCOL(protocol)) ? 1+ITEM_MODEL_SETUP_MAX : ITEM_MODEL_SETUP_MAX) + uint8_t protocol = g_model.protocol; + MENU_TAB({ 0, 0, CASE_PCBTARANIS(0) 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS-1, FIELD_PROTOCOL_MAX, 2 }); +#else + #define CURSOR_ON_CELL (true) + #define MODEL_SETUP_MAX_LINES ((IS_PPM_PROTOCOL(protocol)||IS_DSM2_PROTOCOL(protocol)||IS_PXX_PROTOCOL(protocol)) ? 1+ITEM_MODEL_SETUP_MAX : ITEM_MODEL_SETUP_MAX) + uint8_t protocol = g_model.protocol; + MENU_TAB({ 0, 0, CASE_PCBTARANIS(0) 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 0, 1, 0, 0, 0, 0, 0, NUM_SWITCHES, NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS-1, FIELD_PROTOCOL_MAX, 2, CASE_PCBSKY9X(1) CASE_PCBSKY9X(2) }); +#endif + + MENU_CHECK(menuTabModel, e_ModelSetup, MODEL_SETUP_MAX_LINES); + +#if defined(CPUARM) && (defined(DSM2) || defined(PXX)) + if (menuEvent) { + moduleFlag[0] = 0; +#if NUM_MODULES > 1 + moduleFlag[1] = 0; +#endif + } +#endif + + TITLE(STR_MENUSETUP); + + uint8_t sub = m_posVert - 1; + int8_t editMode = s_editMode; + + for (uint8_t i=0; i0) ? BLINK|INVERS : INVERS); + uint8_t attr = (sub == k ? blink : 0); + + switch(k) { + case ITEM_MODEL_NAME: + editSingleName(MODEL_SETUP_2ND_COLUMN, y, STR_MODELNAME, g_model.header.name, sizeof(g_model.header.name), event, attr); +#if defined(CPUARM) + memcpy(modelHeaders[g_eeGeneral.currModel].name, g_model.header.name, sizeof(g_model.header.name)); +#endif + break; + +#if defined(PCBTARANIS) && defined(SDCARD) + case ITEM_MODEL_BITMAP: + lcd_putsLeft(y, STR_BITMAP); + if (ZEXIST(g_model.header.bitmap)) + lcd_putsnAtt(MODEL_SETUP_2ND_COLUMN, y, g_model.header.bitmap, sizeof(g_model.header.bitmap), attr); + else + lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_VCSWFUNC, 0, attr); + if (attr && event==EVT_KEY_BREAK(KEY_ENTER) && READ_ONLY_UNLOCKED()) { + s_editMode = 0; + if (listSdFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap), g_model.header.bitmap, LIST_NONE_SD_FILE)) { + menuHandler = onModelSetupBitmapMenu; + } + else { + POPUP_WARNING(STR_NO_BITMAPS_ON_SD); + s_menu_flags = 0; + } + } + break; +#endif + +#if defined(CPUARM) + case ITEM_MODEL_TIMER1: + case ITEM_MODEL_TIMER2: + case ITEM_MODEL_TIMER3: + { + unsigned int timerIdx = (k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)); + TimerData * timer = &g_model.timers[timerIdx]; + putsStrIdx(0*FW, y, STR_TIMER, timerIdx+1); + putsTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, m_posHorz==0 ? attr : 0); + putsTimer(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, m_posHorz==1 ? attr : 0, m_posHorz==2 ? attr : 0); +#if defined(PCBTARANIS) + if (attr && m_posHorz < 0) lcd_filled_rect(MODEL_SETUP_2ND_COLUMN, y, LCD_W-MODEL_SETUP_2ND_COLUMN-MENUS_SCROLLBAR_WIDTH, 8); +#endif + if (attr && (editMode>0 || p1valdiff)) { + div_t qr = div(timer->start, 60); + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR_CHECK(event, timer->mode, SWSRC_FIRST, TMRMODE_COUNT+SWSRC_LAST-1/*SWSRC_None removed*/, isSwitchAvailableInTimers); + break; + case 1: + CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 59); + timer->start = qr.rem + qr.quot*60; + break; + case 2: + qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62)-2; + timer->start -= qr.rem ; + if ((int16_t)timer->start < 0) timer->start=0; + break; + } + } + break; + } + + case ITEM_MODEL_TIMER1_NAME: + case ITEM_MODEL_TIMER2_NAME: + case ITEM_MODEL_TIMER3_NAME: + { + TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)]; + editSingleName(MODEL_SETUP_2ND_COLUMN, y, STR_TIMER_NAME, timer->name, sizeof(timer->name), event, attr); + break; + } + + case ITEM_MODEL_TIMER1_MINUTE_BEEP: + case ITEM_MODEL_TIMER2_MINUTE_BEEP: + case ITEM_MODEL_TIMER3_MINUTE_BEEP: + { + TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)]; + timer->minuteBeep = onoffMenuItem(timer->minuteBeep, MODEL_SETUP_2ND_COLUMN, y, STR_MINUTEBEEP, attr, event); + break; + } + + case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP: + case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP: + case ITEM_MODEL_TIMER3_COUNTDOWN_BEEP: + { + TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)]; + timer->countdownBeep = selectMenuItem(MODEL_SETUP_2ND_COLUMN, y, STR_BEEPCOUNTDOWN, STR_VBEEPCOUNTDOWN, timer->countdownBeep, 0, 2, attr, event); + break; + } + + case ITEM_MODEL_TIMER1_PERSISTENT: + case ITEM_MODEL_TIMER2_PERSISTENT: + case ITEM_MODEL_TIMER3_PERSISTENT: + { + TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)]; + timer->persistent = selectMenuItem(MODEL_SETUP_2ND_COLUMN, y, STR_PERSISTENT, STR_VPERSISTENT, timer->persistent, 0, 2, attr, event); + break; + } +#else + case ITEM_MODEL_TIMER1: + case ITEM_MODEL_TIMER2: + case ITEM_MODEL_TIMER1_MINUTE_BEEP: + case ITEM_MODEL_TIMER2_MINUTE_BEEP: + case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP: + case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP: + { + TimerData *timer = &g_model.timers[k>=ITEM_MODEL_TIMER2 ? 1 : 0]; + if (k==ITEM_MODEL_TIMER1_MINUTE_BEEP || k==ITEM_MODEL_TIMER2_MINUTE_BEEP) { + timer->minuteBeep = onoffMenuItem(timer->minuteBeep, MODEL_SETUP_2ND_COLUMN, y, STR_MINUTEBEEP, attr, event); + } + else if (k==ITEM_MODEL_TIMER1_COUNTDOWN_BEEP || k==ITEM_MODEL_TIMER2_COUNTDOWN_BEEP) { + timer->countdownBeep = onoffMenuItem(timer->countdownBeep, MODEL_SETUP_2ND_COLUMN, y, STR_BEEPCOUNTDOWN, attr, event); + } + else { + putsStrIdx(0*FW, y, STR_TIMER, k>=ITEM_MODEL_TIMER2 ? 2 : 1); + putsTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, m_posHorz==0 ? attr : 0); + putsTimer(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, m_posHorz==1 ? attr : 0, m_posHorz==2 ? attr : 0); + if (attr && (editMode>0 || p1valdiff)) { + div_t qr = div(timer->start, 60); + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR_CHECK(event, timer->mode, SWSRC_FIRST, TMRMODE_COUNT+SWSRC_LAST-1/*SWSRC_None removed*/, isSwitchAvailableInTimers); + break; + case 1: + CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 59); + timer->start = qr.rem + qr.quot*60; + break; + case 2: + qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62)-2; + timer->start -= qr.rem ; + break; + } + } + } + break; + } + +#if defined(PCBGRUVIN9X) + case ITEM_MODEL_TIMER1_PERSISTENT: + case ITEM_MODEL_TIMER2_PERSISTENT: + { + TimerData &timer = g_model.timers[k==ITEM_MODEL_TIMER2_PERSISTENT]; + timer.persistent = selectMenuItem(MODEL_SETUP_2ND_COLUMN, y, STR_PERSISTENT, STR_VPERSISTENT, timer.persistent, 0, 2, attr, event); + break; + } +#endif +#endif + + case ITEM_MODEL_EXTENDED_LIMITS: + ON_OFF_MENU_ITEM(g_model.extendedLimits, MODEL_SETUP_2ND_COLUMN, y, STR_ELIMITS, attr, event); + break; + + case ITEM_MODEL_EXTENDED_TRIMS: +#if defined(CPUM64) + ON_OFF_MENU_ITEM(g_model.extendedTrims, MODEL_SETUP_2ND_COLUMN, y, STR_ETRIMS, attr, event); +#else + ON_OFF_MENU_ITEM(g_model.extendedTrims, MODEL_SETUP_2ND_COLUMN, y, STR_ETRIMS, m_posHorz<=0 ? attr : 0, event==EVT_KEY_BREAK(KEY_ENTER) ? event : 0); + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_RESET_BTN, m_posHorz>0 && !s_noHi ? attr : 0); + if (attr && m_posHorz>0) { + s_editMode = 0; + if (event==EVT_KEY_LONG(KEY_ENTER)) { + s_noHi = NO_HI_LEN; + for (uint8_t i=0; i MIXSRC_Thr) + idx += 1; + if (idx >= MIXSRC_FIRST_POT+NUM_POTS) + idx += MIXSRC_CH1 - MIXSRC_FIRST_POT - NUM_POTS; + putsMixerSource(MODEL_SETUP_2ND_COLUMN, y, idx, attr); + break; + } + + case ITEM_MODEL_THROTTLE_TRIM: + ON_OFF_MENU_ITEM(g_model.thrTrim, MODEL_SETUP_2ND_COLUMN, y, STR_TTRIM, attr, event); + break; + +#if defined(CPUARM) + case ITEM_MODEL_PREFLIGHT_LABEL: + lcd_putsLeft(y, STR_PREFLIGHT); + break; + + case ITEM_MODEL_CHECKLIST_DISPLAY: + ON_OFF_MENU_ITEM(g_model.displayChecklist, MODEL_SETUP_2ND_COLUMN, y, STR_CHECKLIST, attr, event); + break; +#endif + + case ITEM_MODEL_THROTTLE_WARNING: + g_model.disableThrottleWarning = !onoffMenuItem(!g_model.disableThrottleWarning, MODEL_SETUP_2ND_COLUMN, y, STR_THROTTLEWARNING, attr, event); + break; + +#if defined(PCBTARANIS) + // TODO something more generic + case ITEM_MODEL_SWITCHES_WARNING2: + if (i==0) s_pgOfs++; + break; +#endif + + case ITEM_MODEL_SWITCHES_WARNING: +#if defined(PCBTARANIS) + // TODO something more generic + if (i==LCD_LINES-2) { + s_pgOfs++; + break; + } +#endif + { + lcd_putsLeft(y, STR_SWITCHWARNING); + swarnstate_t states = g_model.switchWarningState; + char c; + if (attr) { + s_editMode = 0; + if (!READ_ONLY()) { + switch (event) { + CASE_EVT_ROTARY_BREAK + case EVT_KEY_BREAK(KEY_ENTER): +#if defined(CPUM64) + g_model.switchWarningEnable ^= (1 << m_posHorz); + eeDirty(EE_MODEL); +#elif !defined(PCBTARANIS) + if (m_posHorz < NUM_SWITCHES-1) { + g_model.switchWarningEnable ^= (1 << m_posHorz); + eeDirty(EE_MODEL); + } +#endif + break; + + case EVT_KEY_LONG(KEY_ENTER): +#if defined(CPUM64) + getMovedSwitch(); + g_model.switchWarningState = switches_states; + AUDIO_WARNING1(); + eeDirty(EE_MODEL); +#elif defined(PCBTARANIS) + if (m_posHorz < 0) { + s_noHi = NO_HI_LEN; + getMovedSwitch(); + g_model.switchWarningState = switches_states; + AUDIO_WARNING1(); + eeDirty(EE_MODEL); + } +#else + if (m_posHorz == NUM_SWITCHES-1) { + s_noHi = NO_HI_LEN; + getMovedSwitch(); + g_model.switchWarningState = switches_states; + AUDIO_WARNING1(); + eeDirty(EE_MODEL); + } +#endif + killEvents(event); + break; + } + } + } + + LcdFlags line = attr; + +#if defined(PCBTARANIS) + for (int i=0, current=0; i>= 2; + } + if (attr && m_posHorz < 0) { + lcd_filled_rect(MODEL_SETUP_2ND_COLUMN-1, y-1, 8*(2*FW+1), ONE_2x2POS_DEFINED() ? 2*FH+1 : FH+1); + } +#else + for (uint8_t i=0; i>= 2; + } + else { + if ((states & 0x01) && swactive) + attr = INVERS; + c = pgm_read_byte(STR_VSWITCHES - 2 + 9 + (3*(i+1))); + states >>= 1; + } + if (line && (m_posHorz == i)) { + attr = BLINK; + if (swactive) + attr |= INVERS; + } + lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+i*FW, y, (swactive || (attr & BLINK)) ? c : '-', attr); +#if !defined(CPUM64) + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+(NUM_SWITCHES*FW), y, PSTR("<]"), (m_posHorz == NUM_SWITCHES-1 && !s_noHi) ? line : 0); +#endif + } +#endif + break; + } + +#if defined(PCBTARANIS) + case ITEM_MODEL_POT_WARNING: + { + lcd_putsLeft(y, STR_POTWARNING); + uint8_t potMode = g_model.nPotsToWarn >> 6; + if (attr) { + if (m_posHorz) s_editMode = 0; + if (!READ_ONLY() && m_posHorz) { + switch (event) { + case EVT_KEY_LONG(KEY_ENTER): + killEvents(event); + if (potMode == 1) { + SAVE_POT_POSITION(m_posHorz-1); + AUDIO_WARNING1(); + eeDirty(EE_MODEL); + } + break; + case EVT_KEY_BREAK(KEY_ENTER): + g_model.nPotsToWarn ^= (1 << (m_posHorz-1)); + eeDirty(EE_MODEL); + break; + } + } + } + lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, PSTR("\004""OFF\0""Man\0""Auto"), potMode, attr & ((m_posHorz == 0) ? attr : !INVERS)); + if (potMode) { + coord_t x = MODEL_SETUP_2ND_COLUMN+5*FW; + for (uint8_t i=0; i POT3) { + x -= FW; + } +#endif + lcd_putsiAtt(x, y, STR_RETA123, i, ((m_posHorz==i) && attr) ? BLINK|INVERS : (((g_model.beepANACenter & ((BeepANACenter)1<0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + g_model.externalModule = checkIncDec(event, g_model.externalModule, MODULE_TYPE_NONE, MODULE_TYPE_COUNT-1, EE_MODEL, isModuleAvailable); + if (checkIncDec_Ret) { + g_model.moduleData[EXTERNAL_MODULE].rfProtocol = 0; + g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0; + if (g_model.externalModule == MODULE_TYPE_PPM) + g_model.moduleData[EXTERNAL_MODULE].channelsCount = 0; + else + g_model.moduleData[EXTERNAL_MODULE].channelsCount = MAX_EXTERNAL_MODULE_CHANNELS(); + } + break; + case 1: + if (IS_MODULE_DSM2(EXTERNAL_MODULE)) + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, DSM2_PROTO_LP45, DSM2_PROTO_DSMX); + else + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, RF_PROTO_X16, RF_PROTO_LAST); + if (checkIncDec_Ret) { + g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0; + g_model.moduleData[EXTERNAL_MODULE].channelsCount = MAX_EXTERNAL_MODULE_CHANNELS(); + } + } + } + break; +#endif + +#if defined(PCBTARANIS) + case ITEM_MODEL_TRAINER_LABEL: + lcd_putsLeft(y, STR_TRAINER); + break; + + case ITEM_MODEL_INTERNAL_MODULE_CHANNELS: +#elif defined(PCBSKY9X) && !defined(REVX) + case ITEM_MODEL_EXTRA_MODULE_CHANNELS: +#endif +#if defined(CPUARM) + case ITEM_MODEL_EXTERNAL_MODULE_CHANNELS: +#if defined(PCBTARANIS) + case ITEM_MODEL_TRAINER_CHANNELS: +#endif + { + uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); + ModuleData & moduleData = g_model.moduleData[moduleIdx]; + lcd_putsLeft(y, STR_CHANNELRANGE); + if ((int8_t)PORT_CHANNELS_ROWS(moduleIdx) >= 0) { + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN, y, STR_CH, m_posHorz==0 ? attr : 0); + lcd_outdezAtt(lcdLastPos, y, moduleData.channelsStart+1, LEFT | (m_posHorz==0 ? attr : 0)); + lcd_putc(lcdLastPos, y, '-'); + lcd_outdezAtt(lcdLastPos + FW+1, y, moduleData.channelsStart+NUM_CHANNELS(moduleIdx), LEFT | (m_posHorz==1 ? attr : 0)); + if (attr && (editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.channelsStart, 32-8-moduleData.channelsCount); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, moduleData.channelsCount, -4, min(MAX_CHANNELS(moduleIdx), 32-8-moduleData.channelsStart)); + if ((k == ITEM_MODEL_EXTERNAL_MODULE_CHANNELS && g_model.externalModule == MODULE_TYPE_PPM) +#if defined(PCBTARANIS) + || (k == ITEM_MODEL_TRAINER_CHANNELS) +#endif + ) + SET_DEFAULT_PPM_FRAME_LENGTH(moduleIdx); + break; + } + } + } + break; + } +#endif + +#if defined(PCBTARANIS) + case ITEM_MODEL_INTERNAL_MODULE_BIND: +#elif defined(PCBSKY9X) && !defined(REVX) + case ITEM_MODEL_EXTRA_MODULE_BIND: +#endif +#if defined(CPUARM) + case ITEM_MODEL_EXTERNAL_MODULE_BIND: +#if defined(PCBTARANIS) + case ITEM_MODEL_TRAINER_SETTINGS: +#endif + { + uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); + ModuleData & moduleData = g_model.moduleData[moduleIdx]; + if (IS_MODULE_PPM(moduleIdx)) { + lcd_putsLeft(y, STR_PPMFRAME); + lcd_puts(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppmFrameLength*5 + 225, (m_posHorz<=0 ? attr : 0) | PREC1|LEFT); + lcd_putc(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u'); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (moduleData.ppmDelay*50)+300, (CURSOR_ON_LINE() || m_posHorz==1) ? attr : 0); + lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+10*FW, y, moduleData.ppmPulsePol ? '+' : '-', (CURSOR_ON_LINE() || m_posHorz==2) ? attr : 0); + + if (attr && (editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR(event, moduleData.ppmFrameLength, -20, 35); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, moduleData.ppmDelay, -4, 10); + break; + case 2: + CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.ppmPulsePol, 1); + break; + } + } + } + else { + horzpos_t l_posHorz = m_posHorz; + coord_t xOffsetBind = MODEL_SETUP_BIND_OFS; + if (IS_MODULE_XJT(moduleIdx) && IS_D8_RX(moduleIdx)) { + xOffsetBind = 0; + lcd_putsLeft(y, INDENT "Receiver"); + if (attr) l_posHorz += 1; + } + else { + lcd_putsLeft(y, STR_RXNUM); + } + if (IS_MODULE_XJT(moduleIdx) || IS_MODULE_DSM2(moduleIdx)) { + if (xOffsetBind) lcd_outdezNAtt(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId, (l_posHorz==0 ? attr : 0) | LEADING0|LEFT, 2); + if (attr && l_posHorz==0) { + if (editMode>0 || p1valdiff) { + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId, IS_MODULE_DSM2(moduleIdx) ? 20 : 63); + if (checkIncDec_Ret) { + modelHeaders[g_eeGeneral.currModel].modelId = g_model.header.modelId; + } + } + if (editMode==0 && event==EVT_KEY_BREAK(KEY_ENTER)) { + checkModelIdUnique(g_eeGeneral.currModel); + } + } + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+xOffsetBind, y, STR_MODULE_BIND, l_posHorz==1 ? attr : 0); + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+MODEL_SETUP_RANGE_OFS+xOffsetBind, y, STR_MODULE_RANGE, l_posHorz==2 ? attr : 0); + uint8_t newFlag = 0; + if (attr && l_posHorz>0 && s_editMode>0) { + if (l_posHorz == 1) + newFlag = MODULE_BIND; + else if (l_posHorz == 2) { + newFlag = MODULE_RANGECHECK; + } + } + moduleFlag[moduleIdx] = newFlag; + } + } + break; + } +#endif + +#if defined(PCBTARANIS) + case ITEM_MODEL_INTERNAL_MODULE_FAILSAFE: +#endif +#if defined(CPUARM) + case ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE: + { + uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); + ModuleData & moduleData = g_model.moduleData[moduleIdx]; + lcd_putsLeft(y, TR_FAILSAFE); + if (IS_MODULE_XJT(moduleIdx)) { + lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_VFAILSAFE, moduleData.failsafeMode, m_posHorz==0 ? attr : 0); + if (moduleData.failsafeMode == FAILSAFE_CUSTOM) lcd_putsAtt(MODEL_SETUP_2ND_COLUMN + MODEL_SETUP_SET_FAILSAFE_OFS, y, STR_SET, m_posHorz==1 ? attr : 0); + if (attr) { + if (moduleData.failsafeMode != FAILSAFE_CUSTOM) + m_posHorz = 0; + if (m_posHorz==0) { + if (editMode>0 || p1valdiff) { + CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.failsafeMode, FAILSAFE_LAST); + if (checkIncDec_Ret) SEND_FAILSAFE_NOW(moduleIdx); + } + } + else if (m_posHorz==1) { + s_editMode = 0; + if (moduleData.failsafeMode==FAILSAFE_CUSTOM && event==EVT_KEY_FIRST(KEY_ENTER)) { + g_moduleIdx = moduleIdx; + pushMenu(menuModelFailsafe); + } + } + else { + lcd_filled_rect(MODEL_SETUP_2ND_COLUMN, y, LCD_W-MODEL_SETUP_2ND_COLUMN-MENUS_SCROLLBAR_WIDTH, 8); + } + } + } + break; + } +#endif + +#if !defined(CPUARM) + case ITEM_MODEL_PPM1_PROTOCOL: + lcd_putsLeft(y, NO_INDENT(STR_PROTO)); + lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_VPROTOS, protocol, m_posHorz<=0 ? attr : 0); + if (IS_PPM_PROTOCOL(protocol)) { + lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN+7*FW, y, STR_NCHANNELS, g_model.ppmNCH+2, m_posHorz!=0 ? attr : 0); + } + else if (m_posHorz>0 && attr) { + MOVE_CURSOR_FROM_HERE(); + } + if (attr && (editMode>0 || p1valdiff || (!IS_PPM_PROTOCOL(protocol) && !IS_DSM2_PROTOCOL(protocol)))) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.protocol, PROTO_MAX-1); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, g_model.ppmNCH, -2, 4); + g_model.ppmFrameLength = g_model.ppmNCH * 8; + break; + } + } + break; +#endif + +#if 0 + case ITEM_MODEL_PPM2_PROTOCOL: + lcd_putsLeft(y, PSTR("Port2")); + lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_VPROTOS, 0, 0); + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+4*FW+3, y, STR_CH, m_posHorz<=0 ? attr : 0); + lcd_outdezAtt(lcdLastPos, y, g_model.moduleData[1].channelsStart+1, LEFT | (m_posHorz<=0 ? attr : 0)); + lcd_putc(lcdLastPos, y, '-'); + lcd_outdezAtt(lcdLastPos + FW+1, y, g_model.moduleData[1].channelsStart+8+g_model.moduleData[1].channelsCount, LEFT | (m_posHorz!=0 ? attr : 0)); + if (attr && (editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[1].channelsStart, 32-8-g_model.moduleData[1].channelsCount); + SET_DEFAULT_PPM_FRAME_LENGTH(1); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].channelsCount, -4, min(8, 32-8-g_model.moduleData[1].channelsStart)); + SET_DEFAULT_PPM_FRAME_LENGTH(1); + break; + } + } + break; + + case ITEM_MODEL_PPM2_PARAMS: + lcd_putsLeft(y, STR_PPMFRAME); + lcd_puts(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.moduleData[1].ppmFrameLength*5 + 225, (m_posHorz<=0 ? attr : 0) | PREC1|LEFT); + lcd_putc(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u'); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.moduleData[1].ppmDelay*50)+300, (m_posHorz < 0 || m_posHorz==1) ? attr : 0); + lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.moduleData[1].ppmPulsePol ? '+' : '-', (m_posHorz < 0 || m_posHorz==2) ? attr : 0); + if (attr && (editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].ppmFrameLength, -20, 35); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].ppmDelay, -4, 10); + break; + case 2: + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[1].ppmPulsePol, 1); + break; + } + } + break; +#endif + +#if !defined(CPUARM) + case ITEM_MODEL_PPM1_PARAMS: + if (IS_PPM_PROTOCOL(protocol)) { + lcd_putsLeft(y, STR_PPMFRAME); + lcd_puts(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.ppmFrameLength*5 + 225, (m_posHorz<=0 ? attr : 0) | PREC1|LEFT); + lcd_putc(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u'); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.ppmDelay*50)+300, (CURSOR_ON_LINE() || m_posHorz==1) ? attr : 0); + lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.pulsePol ? '+' : '-', (CURSOR_ON_LINE() || m_posHorz==2) ? attr : 0); + if (attr && (editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR(event, g_model.ppmFrameLength, -20, 35); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, g_model.ppmDelay, -4, 10); + break; + case 2: + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.pulsePol, 1); + break; + } + } + } +#if defined(DSM2) || defined(PXX) + else if (IS_DSM2_PROTOCOL(protocol) || IS_PXX_PROTOCOL(protocol)) { + if (attr && m_posHorz > 1) { + REPEAT_LAST_CURSOR_MOVE(); // limit 3 column row to 2 colums (Rx_Num and RANGE fields) + } + + lcd_putsLeft(y, STR_RXNUM); + lcd_outdezNAtt(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId, (m_posHorz<=0 ? attr : 0) | LEADING0|LEFT, 2); + if (attr && (m_posHorz==0 && (editMode>0 || p1valdiff))) { + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId, 99); + } +#if defined(PXX) + if (protocol == PROTO_PXX) { + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_SYNCMENU, m_posHorz!=0 ? attr : 0); + uint8_t newFlag = 0; + if (attr && m_posHorz>0 && editMode>0) { + // send reset code + newFlag = MODULE_BIND; + } + moduleFlag[0] = newFlag; + } +#endif +#if defined(DSM2) + if (IS_DSM2_PROTOCOL(protocol)) { + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_MODULE_RANGE, m_posHorz!=0 ? attr : 0); + moduleFlag[0] = (attr && m_posHorz>0 && editMode>0) ? MODULE_RANGECHECK : 0; // [MENU] key toggles range check mode + } +#endif + } +#endif + break; +#endif + } + } + +#if defined(CPUARM) && defined(PXX) + if (IS_RANGECHECK_ENABLE()) { + displayPopup("RSSI: "); + lcd_outdezAtt(16+4*FW, 5*FH, TELEMETRY_RSSI(), BOLD); + } +#endif +} + +#if defined(CPUARM) +void menuModelFailsafe(uint8_t event) +{ + static bool longNames = false; + bool newLongNames = false; + uint8_t ch = 0; + + if (event == EVT_KEY_LONG(KEY_ENTER) && s_editMode) { + s_noHi = NO_HI_LEN; + g_model.moduleData[g_moduleIdx].failsafeChannels[m_posVert] = channelOutputs[m_posVert]; + eeDirty(EE_MODEL); + AUDIO_WARNING1(); + SEND_FAILSAFE_NOW(g_moduleIdx); + } + + SIMPLE_SUBMENU_NOTITLE(NUM_CHNOUT); + + SET_SCROLLBAR_X(0); + +#if LCD_W >= 212 + #define COL_W (LCD_W/2) + const uint8_t SLIDER_W = 64; + // Column separator + lcd_vline(LCD_W/2, FH, LCD_H-FH); + + if (m_posVert >= 16) { + ch = 16; + } +#else + #define COL_W (LCD_W) + const uint8_t SLIDER_W = 90; + ch = 8 * (m_posVert / 8); +#endif + + lcd_putsCenter(0*FH, FAILSAFESET); + lcd_invert_line(0); + +#if LCD_W >= 212 + for (uint8_t col=0; col<2; col++) +#else + uint8_t col = 0; +#endif + { + coord_t x = col*COL_W+1; + + // Channels + for (uint8_t line=0; line<8; line++) { + coord_t y = 9+line*7; + int32_t val; + uint8_t ofs = (col ? 0 : 1); + + if (ch < g_model.moduleData[g_moduleIdx].channelsStart || ch >= NUM_CHANNELS(g_moduleIdx) + g_model.moduleData[g_moduleIdx].channelsStart) + val = 0; + else if (s_editMode && m_posVert == ch) + val = channelOutputs[ch]; + else + val = g_model.moduleData[g_moduleIdx].failsafeChannels[8*col+line]; + +#if defined(PCBTARANIS) + // Channel name if present, number if not + uint8_t lenLabel = ZLEN(g_model.limitData[ch].name); + if (lenLabel > 4) { + newLongNames = longNames = true; + } + + if (lenLabel > 0) + lcd_putsnAtt(x+1-ofs, y, g_model.limitData[ch].name, sizeof(g_model.limitData[ch].name), ZCHAR | SMLSIZE); + else + putsChn(x+1-ofs, y, ch+1, SMLSIZE); +#else + putsChn(x+1-ofs, y, ch+1, SMLSIZE); +#endif + + // Value + LcdFlags flags = TINSIZE; + if (m_posVert == ch && !s_noHi) { + flags |= INVERS; + if (s_editMode) + flags |= BLINK; + } +#if defined(PPM_UNIT_US) + uint8_t wbar = (longNames ? SLIDER_W-10 : SLIDER_W); + lcd_outdezAtt(x+COL_W-4-wbar-ofs, y, PPM_CH_CENTER(ch)+val/2, flags); +#elif defined(PPM_UNIT_PERCENT_PREC1) + uint8_t wbar = (longNames ? SLIDER_W-16 : SLIDER_W-6); + lcd_outdezAtt(x+COL_W-4-wbar-ofs, y, calcRESXto1000(val), PREC1|flags); +#else + uint8_t wbar = (longNames ? SLIDER_W-10 : SLIDER_W); + lcd_outdezAtt(x+COL_W-4-wbar-ofs, y, calcRESXto1000(val)/10, flags); +#endif + + // Gauge + lcd_rect(x+COL_W-3-wbar-ofs, y, wbar+1, 6); + uint16_t lim = g_model.extendedLimits ? 640*2 : 512*2; + uint8_t len = limit((uint8_t)1, uint8_t((abs(val) * wbar/2 + lim/2) / lim), uint8_t(wbar/2)); + coord_t x0 = (val>0) ? x+COL_W-ofs-3-wbar/2 : x+COL_W-ofs-2-wbar/2-len; + lcd_hline(x0, y+1, len); + lcd_hline(x0, y+2, len); + lcd_hline(x0, y+3, len); + lcd_hline(x0, y+4, len); + + ch++; + } + } + + longNames = newLongNames; +} +#endif