5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
23 vertpos_t menuVerticalOffset
;
25 uint8_t noHighlightCounter
;
26 uint8_t menuCalibrationState
;
27 vertpos_t menuVerticalPosition
;
28 horzpos_t menuHorizontalPosition
;
30 #if defined(NAVIGATION_POT1)
34 #if defined(NAVIGATION_POT2)
38 int8_t checkIncDec_Ret
;
42 #define DBLKEYS_PRESSED_RGT_LFT(in) ((in & ((1<<KEY_SHIFT) + (1<<KEY_UP))) == ((1<<KEY_SHIFT) + (1<<KEY_UP)))
44 #define DBLKEYS_PRESSED_UP_DWN(in) ((in & ((1<<KEY_SHIFT) + (1<<KEY_DOWN))) == ((1<<KEY_SHIFT) + (1<<KEY_DOWN)))
46 #define DBLKEYS_PRESSED_RGT_UP(in) ((in & ((1<<KEY_SHIFT) + (1<<KEY_RIGHT))) == ((1<<KEY_SHIFT) + (1<<KEY_RIGHT)))
48 #define DBLKEYS_PRESSED_LFT_DWN(in) ((in & ((1<<KEY_SHIFT) + (1<<KEY_LEFT))) == ((1<<KEY_SHIFT) + (1<<KEY_LEFT)))
50 #define DBLKEYS_PRESSED_RGT_LFT(in) (false)
51 #define DBLKEYS_PRESSED_UP_DWN(in) (false)
52 #define DBLKEYS_PRESSED_RGT_UP(in) (false)
53 #define DBLKEYS_PRESSED_LFT_DWN(in) (false)
55 #define DBLKEYS_PRESSED_RGT_LFT(in) ((in & (KEYS_GPIO_PIN_RIGHT + KEYS_GPIO_PIN_LEFT)) == (KEYS_GPIO_PIN_RIGHT + KEYS_GPIO_PIN_LEFT))
56 #define DBLKEYS_PRESSED_UP_DWN(in) ((in & (KEYS_GPIO_PIN_UP + KEYS_GPIO_PIN_DOWN)) == (KEYS_GPIO_PIN_UP + KEYS_GPIO_PIN_DOWN))
57 #define DBLKEYS_PRESSED_RGT_UP(in) ((in & (KEYS_GPIO_PIN_RIGHT + KEYS_GPIO_PIN_UP)) == (KEYS_GPIO_PIN_RIGHT + KEYS_GPIO_PIN_UP))
58 #define DBLKEYS_PRESSED_LFT_DWN(in) ((in & (KEYS_GPIO_PIN_LEFT + KEYS_GPIO_PIN_DOWN)) == (KEYS_GPIO_PIN_LEFT + KEYS_GPIO_PIN_DOWN))
62 INIT_STOPS(stops100
, 3, -100, 0, 100)
63 INIT_STOPS(stops1000
, 3, -1000, 0, 1000)
64 INIT_STOPS(stopsSwitch
, 15, SWSRC_FIRST
, CATEGORY_END(-SWSRC_FIRST_LOGICAL_SWITCH
), CATEGORY_END(-SWSRC_FIRST_TRIM
), CATEGORY_END(-SWSRC_LAST_SWITCH
+1), 0, CATEGORY_END(SWSRC_LAST_SWITCH
), CATEGORY_END(SWSRC_FIRST_TRIM
-1), CATEGORY_END(SWSRC_FIRST_LOGICAL_SWITCH
-1), SWSRC_LAST
)
66 #if defined(PCBTARANIS)
67 int checkIncDecSelection
= 0;
69 void onSourceLongEnterPress(const char * result
)
71 if (result
== STR_MENU_INPUTS
)
72 checkIncDecSelection
= getFirstAvailable(MIXSRC_FIRST_INPUT
, MIXSRC_LAST_INPUT
, isInputAvailable
)+1;
73 #if defined(LUA_MODEL_SCRIPTS)
74 else if (result
== STR_MENU_LUA
)
75 checkIncDecSelection
= getFirstAvailable(MIXSRC_FIRST_LUA
, MIXSRC_LAST_LUA
, isSourceAvailable
);
77 else if (result
== STR_MENU_STICKS
)
78 checkIncDecSelection
= MIXSRC_FIRST_STICK
;
79 else if (result
== STR_MENU_POTS
)
80 checkIncDecSelection
= MIXSRC_FIRST_POT
;
81 else if (result
== STR_MENU_MAX
)
82 checkIncDecSelection
= MIXSRC_MAX
;
83 else if (result
== STR_MENU_HELI
)
84 checkIncDecSelection
= MIXSRC_FIRST_HELI
;
85 else if (result
== STR_MENU_TRIMS
)
86 checkIncDecSelection
= MIXSRC_FIRST_TRIM
;
87 else if (result
== STR_MENU_SWITCHES
)
88 checkIncDecSelection
= MIXSRC_FIRST_SWITCH
;
89 else if (result
== STR_MENU_TRAINER
)
90 checkIncDecSelection
= MIXSRC_FIRST_TRAINER
;
91 else if (result
== STR_MENU_CHANNELS
)
92 checkIncDecSelection
= getFirstAvailable(MIXSRC_FIRST_CH
, MIXSRC_LAST_CH
, isSourceAvailable
);
93 else if (result
== STR_MENU_GVARS
)
94 checkIncDecSelection
= MIXSRC_FIRST_GVAR
;
95 else if (result
== STR_MENU_TELEMETRY
) {
96 for (int i
= 0; i
< MAX_TELEMETRY_SENSORS
; i
++) {
97 TelemetrySensor
* sensor
= & g_model
.telemetrySensors
[i
];
98 if (sensor
->isAvailable()) {
99 checkIncDecSelection
= MIXSRC_FIRST_TELEM
+ 3*i
;
106 void onSwitchLongEnterPress(const char * result
)
108 if (result
== STR_MENU_SWITCHES
)
109 checkIncDecSelection
= SWSRC_FIRST_SWITCH
;
110 else if (result
== STR_MENU_TRIMS
)
111 checkIncDecSelection
= SWSRC_FIRST_TRIM
;
112 else if (result
== STR_MENU_LOGICAL_SWITCHES
)
113 checkIncDecSelection
= SWSRC_FIRST_LOGICAL_SWITCH
+ getFirstAvailable(0, MAX_LOGICAL_SWITCHES
, isLogicalSwitchAvailable
);
114 else if (result
== STR_MENU_OTHER
)
115 checkIncDecSelection
= SWSRC_ON
;
116 else if (result
== STR_MENU_INVERT
)
117 checkIncDecSelection
= SWSRC_INVERT
;
122 int checkIncDec(event_t event
, int val
, int i_min
, int i_max
, unsigned int i_flags
, IsValueAvailable isValueAvailable
, const CheckIncDecStops
&stops
)
126 #if 0 // TODO ? defined(DBLKEYS)
127 uint32_t in
= KEYS_PRESSED();
128 if (!(i_flags
& NO_DBLKEYS
) && (EVT_KEY_MASK(event
))) {
130 if (DBLKEYS_PRESSED_RGT_LFT(in
)) {
131 if (!isValueAvailable
|| isValueAvailable(-val
)) {
135 else if (DBLKEYS_PRESSED_RGT_UP(in
)) {
136 newval
= (i_max
> stops
.max() ? stops
.max() : i_max
);
137 while (isValueAvailable
&& !isValueAvailable(newval
) && newval
>i_min
) {
141 else if (DBLKEYS_PRESSED_LFT_DWN(in
)) {
142 newval
= (i_min
< stops
.min() ? stops
.min() : i_min
);
143 while (isValueAvailable
&& !isValueAvailable(newval
) && newval
<i_max
) {
147 else if (DBLKEYS_PRESSED_UP_DWN(in
)) {
156 killEvents(KEY_DOWN
);
157 killEvents(KEY_RIGHT
);
158 killEvents(KEY_LEFT
);
159 killEvents(KEY_PAGE
);
160 killEvents(KEY_MENU
);
161 killEvents(KEY_ENTER
);
162 killEvents(KEY_EXIT
);
168 if (s_editMode
>0 && event
==EVT_ROTARY_RIGHT
) {
169 newval
+= min
<int>(rotencSpeed
, i_max
-val
);
170 while (isValueAvailable
&& !isValueAvailable(newval
) && newval
<=i_max
) {
173 if (newval
> i_max
) {
178 else if (s_editMode
>0 && event
==EVT_ROTARY_LEFT
) {
179 newval
-= min
<int>(rotencSpeed
, val
-i_min
);
180 while (isValueAvailable
&& !isValueAvailable(newval
) && newval
>=i_min
) {
183 if (newval
< i_min
) {
189 if (!READ_ONLY() && i_min
==0 && i_max
==1 && event
==EVT_KEY_BREAK(KEY_ENTER
)) {
194 #if defined(AUTOSWITCH)
195 if (i_flags
& INCDEC_SWITCH
) {
196 newval
= checkIncDecMovedSwitch(newval
);
200 #if defined(AUTOSOURCE)
201 if (i_flags
& INCDEC_SOURCE
) {
203 int source
= GET_MOVED_SOURCE(i_min
, i_max
);
207 #if defined(AUTOSWITCH)
209 unsigned int swtch
= abs(getMovedSwitch());
211 newval
= switchToMix(swtch
);
220 storageDirty(i_flags
& (EE_GENERAL
|EE_MODEL
));
221 checkIncDec_Ret
= (newval
> val
? 1 : -1);
227 if (i_flags
& INCDEC_SOURCE
) {
228 if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
230 checkIncDecSelection
= MIXSRC_NONE
;
232 if (i_min
<= MIXSRC_FIRST_INPUT
&& i_max
>= MIXSRC_FIRST_INPUT
) {
233 if (getFirstAvailable(MIXSRC_FIRST_INPUT
, MIXSRC_LAST_INPUT
, isInputAvailable
) != MIXSRC_NONE
) {
234 POPUP_MENU_ADD_ITEM(STR_MENU_INPUTS
);
237 #if defined(LUA_MODEL_SCRIPTS)
238 if (i_min
<= MIXSRC_FIRST_LUA
&& i_max
>= MIXSRC_FIRST_LUA
) {
239 if (getFirstAvailable(MIXSRC_FIRST_LUA
, MIXSRC_LAST_LUA
, isSourceAvailable
) != MIXSRC_NONE
) {
240 POPUP_MENU_ADD_ITEM(STR_MENU_LUA
);
244 if (i_min
<= MIXSRC_FIRST_STICK
&& i_max
>= MIXSRC_FIRST_STICK
) POPUP_MENU_ADD_ITEM(STR_MENU_STICKS
);
245 if (i_min
<= MIXSRC_FIRST_POT
&& i_max
>= MIXSRC_FIRST_POT
) POPUP_MENU_ADD_ITEM(STR_MENU_POTS
);
246 if (i_min
<= MIXSRC_MAX
&& i_max
>= MIXSRC_MAX
) POPUP_MENU_ADD_ITEM(STR_MENU_MAX
);
248 if (i_min
<= MIXSRC_FIRST_HELI
&& i_max
>= MIXSRC_FIRST_HELI
) POPUP_MENU_ADD_ITEM(STR_MENU_HELI
);
250 if (i_min
<= MIXSRC_FIRST_TRIM
&& i_max
>= MIXSRC_FIRST_TRIM
) POPUP_MENU_ADD_ITEM(STR_MENU_TRIMS
);
251 if (i_min
<= MIXSRC_FIRST_SWITCH
&& i_max
>= MIXSRC_FIRST_SWITCH
) POPUP_MENU_ADD_ITEM(STR_MENU_SWITCHES
);
252 if (i_min
<= MIXSRC_FIRST_TRAINER
&& i_max
>= MIXSRC_FIRST_TRAINER
) POPUP_MENU_ADD_ITEM(STR_MENU_TRAINER
);
253 if (i_min
<= MIXSRC_FIRST_CH
&& i_max
>= MIXSRC_FIRST_CH
) POPUP_MENU_ADD_ITEM(STR_MENU_CHANNELS
);
254 if (i_min
<= MIXSRC_FIRST_GVAR
&& i_max
>= MIXSRC_FIRST_GVAR
&& isValueAvailable(MIXSRC_FIRST_GVAR
)) {
255 POPUP_MENU_ADD_ITEM(STR_MENU_GVARS
);
258 if (i_min
<= MIXSRC_FIRST_TELEM
&& i_max
>= MIXSRC_FIRST_TELEM
) {
259 for (int i
= 0; i
< MAX_TELEMETRY_SENSORS
; i
++) {
260 TelemetrySensor
* sensor
= & g_model
.telemetrySensors
[i
];
261 if (sensor
->isAvailable()) {
262 POPUP_MENU_ADD_ITEM(STR_MENU_TELEMETRY
);
267 POPUP_MENU_START(onSourceLongEnterPress
);
269 if (checkIncDecSelection
!= 0) {
270 newval
= checkIncDecSelection
;
271 if (checkIncDecSelection
!= MIXSRC_MAX
)
272 s_editMode
= EDIT_MODIFY_FIELD
;
273 checkIncDecSelection
= 0;
276 else if (i_flags
& INCDEC_SWITCH
) {
277 if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
279 checkIncDecSelection
= SWSRC_NONE
;
280 if (i_min
<= SWSRC_FIRST_SWITCH
&& i_max
>= SWSRC_LAST_SWITCH
) POPUP_MENU_ADD_ITEM(STR_MENU_SWITCHES
);
281 if (i_min
<= SWSRC_FIRST_TRIM
&& i_max
>= SWSRC_LAST_TRIM
) POPUP_MENU_ADD_ITEM(STR_MENU_TRIMS
);
282 if (i_min
<= SWSRC_FIRST_LOGICAL_SWITCH
&& i_max
>= SWSRC_LAST_LOGICAL_SWITCH
) {
283 for (int i
= 0; i
< MAX_LOGICAL_SWITCHES
; i
++) {
284 if (isValueAvailable
&& isValueAvailable(SWSRC_FIRST_LOGICAL_SWITCH
+i
)) {
285 POPUP_MENU_ADD_ITEM(STR_MENU_LOGICAL_SWITCHES
);
290 if (isValueAvailable
&& isValueAvailable(SWSRC_ON
)) POPUP_MENU_ADD_ITEM(STR_MENU_OTHER
);
291 if (isValueAvailable
&& isValueAvailable(-newval
)) POPUP_MENU_ADD_ITEM(STR_MENU_INVERT
);
292 POPUP_MENU_START(onSwitchLongEnterPress
);
293 s_editMode
= EDIT_MODIFY_FIELD
;
295 if (checkIncDecSelection
!= 0) {
296 newval
= (checkIncDecSelection
== SWSRC_INVERT
? -newval
: checkIncDecSelection
);
297 s_editMode
= EDIT_MODIFY_FIELD
;
298 checkIncDecSelection
= 0;
304 int checkIncDec(event_t event
, int val
, int i_min
, int i_max
, unsigned int i_flags
, IsValueAvailable isValueAvailable
, const CheckIncDecStops
&stops
)
309 uint8_t in
= KEYS_PRESSED();
310 if (!(i_flags
& NO_DBLKEYS
) && (EVT_KEY_MASK(event
))) {
312 if (DBLKEYS_PRESSED_RGT_LFT(in
)) {
313 if (!isValueAvailable
|| isValueAvailable(-val
)) {
317 else if (DBLKEYS_PRESSED_RGT_UP(in
)) {
318 newval
= (i_max
> stops
.max() ? stops
.max() : i_max
);
319 while (isValueAvailable
&& !isValueAvailable(newval
) && newval
>i_min
) {
323 else if (DBLKEYS_PRESSED_LFT_DWN(in
)) {
324 newval
= (i_min
< stops
.min() ? stops
.min() : i_min
);
325 while (isValueAvailable
&& !isValueAvailable(newval
) && newval
<i_max
) {
329 else if (DBLKEYS_PRESSED_UP_DWN(in
)) {
338 killEvents(KEY_DOWN
);
339 killEvents(KEY_RIGHT
);
340 killEvents(KEY_LEFT
);
346 #if defined(PCBXLITE)
347 if (s_editMode
> 0) {
348 if (event
==EVT_KEY_FIRST(KEY_RIGHT
) || event
==EVT_KEY_REPT(KEY_RIGHT
) || (s_editMode
>0 && (event
==EVT_KEY_FIRST(KEY_UP
) || event
==EVT_KEY_REPT(KEY_UP
)))) {
350 if (event
==EVT_KEY_FIRST(KEY_RIGHT
) || event
==EVT_KEY_REPT(KEY_RIGHT
) || (s_editMode
>0 && (IS_ROTARY_RIGHT(event
) || event
==EVT_KEY_FIRST(KEY_UP
) || event
==EVT_KEY_REPT(KEY_UP
)))) {
354 } while (isValueAvailable
&& !isValueAvailable(newval
) && newval
<=i_max
);
356 if (newval
> i_max
) {
362 #if defined(PCBXLITE)
363 else if (event
==EVT_KEY_FIRST(KEY_LEFT
) || event
==EVT_KEY_REPT(KEY_LEFT
) || (s_editMode
>0 && (event
==EVT_KEY_FIRST(KEY_DOWN
) || event
==EVT_KEY_REPT(KEY_DOWN
)))) {
365 else if (event
==EVT_KEY_FIRST(KEY_LEFT
) || event
==EVT_KEY_REPT(KEY_LEFT
) || (s_editMode
>0 && (IS_ROTARY_LEFT(event
) || event
==EVT_KEY_FIRST(KEY_DOWN
) || event
==EVT_KEY_REPT(KEY_DOWN
)))) {
368 if (IS_KEY_REPT(event
) && (i_flags
& INCDEC_REP10
)) {
369 newval
-= min(10, val
-i_min
);
374 } while (isValueAvailable
&& !isValueAvailable(newval
) && newval
>=i_min
);
376 if (newval
< i_min
) {
382 #if defined(PCBXLITE)
386 if (!READ_ONLY() && i_min
==0 && i_max
==1 && (event
==EVT_KEY_BREAK(KEY_ENTER
) || IS_ROTARY_BREAK(event
))) {
391 #if defined(NAVIGATION_POT1)
392 // change values based on P1
397 #if defined(AUTOSWITCH)
398 if (i_flags
& INCDEC_SWITCH
) {
399 newval
= checkIncDecMovedSwitch(newval
);
403 #if defined(AUTOSOURCE)
404 if (i_flags
& INCDEC_SOURCE
) {
406 int8_t source
= GET_MOVED_SOURCE(i_min
, i_max
);
410 #if defined(AUTOSWITCH)
412 uint8_t swtch
= abs(getMovedSwitch());
414 newval
= switchToMix(swtch
);
423 if (!(i_flags
& NO_INCDEC_MARKS
) && (newval
!= i_max
) && (newval
!= i_min
) && (newval
==0 || newval
==-100 || newval
==+100) && !IS_ROTARY_EVENT(event
)) {
424 pauseEvents(event
); // delay before auto-repeat continues
427 storageDirty(i_flags
& (EE_GENERAL
|EE_MODEL
));
428 checkIncDec_Ret
= (newval
> val
? 1 : -1);
434 #if defined(PCBXLITE)
435 if (i_flags
& INCDEC_SOURCE
) {
436 if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
438 checkIncDecSelection
= MIXSRC_NONE
;
440 if (i_min
<= MIXSRC_FIRST_INPUT
&& i_max
>= MIXSRC_FIRST_INPUT
) {
441 if (getFirstAvailable(MIXSRC_FIRST_INPUT
, MIXSRC_LAST_INPUT
, isInputAvailable
) != MIXSRC_NONE
) {
442 POPUP_MENU_ADD_ITEM(STR_MENU_INPUTS
);
445 #if defined(LUA_MODEL_SCRIPTS)
446 if (i_min
<= MIXSRC_FIRST_LUA
&& i_max
>= MIXSRC_FIRST_LUA
) {
447 if (getFirstAvailable(MIXSRC_FIRST_LUA
, MIXSRC_LAST_LUA
, isSourceAvailable
) != MIXSRC_NONE
) {
448 POPUP_MENU_ADD_ITEM(STR_MENU_LUA
);
452 if (i_min
<= MIXSRC_FIRST_STICK
&& i_max
>= MIXSRC_FIRST_STICK
) POPUP_MENU_ADD_ITEM(STR_MENU_STICKS
);
453 if (i_min
<= MIXSRC_FIRST_POT
&& i_max
>= MIXSRC_FIRST_POT
) POPUP_MENU_ADD_ITEM(STR_MENU_POTS
);
454 if (i_min
<= MIXSRC_MAX
&& i_max
>= MIXSRC_MAX
) POPUP_MENU_ADD_ITEM(STR_MENU_MAX
);
456 if (i_min
<= MIXSRC_FIRST_HELI
&& i_max
>= MIXSRC_FIRST_HELI
) POPUP_MENU_ADD_ITEM(STR_MENU_HELI
);
458 if (i_min
<= MIXSRC_FIRST_TRIM
&& i_max
>= MIXSRC_FIRST_TRIM
) POPUP_MENU_ADD_ITEM(STR_MENU_TRIMS
);
459 if (i_min
<= MIXSRC_FIRST_SWITCH
&& i_max
>= MIXSRC_FIRST_SWITCH
) POPUP_MENU_ADD_ITEM(STR_MENU_SWITCHES
);
460 if (i_min
<= MIXSRC_FIRST_TRAINER
&& i_max
>= MIXSRC_FIRST_TRAINER
) POPUP_MENU_ADD_ITEM(STR_MENU_TRAINER
);
461 if (i_min
<= MIXSRC_FIRST_CH
&& i_max
>= MIXSRC_FIRST_CH
) POPUP_MENU_ADD_ITEM(STR_MENU_CHANNELS
);
462 if (i_min
<= MIXSRC_FIRST_GVAR
&& i_max
>= MIXSRC_FIRST_GVAR
&& isValueAvailable(MIXSRC_FIRST_GVAR
)) {
463 POPUP_MENU_ADD_ITEM(STR_MENU_GVARS
);
466 if (i_min
<= MIXSRC_FIRST_TELEM
&& i_max
>= MIXSRC_FIRST_TELEM
) {
467 for (int i
= 0; i
< MAX_TELEMETRY_SENSORS
; i
++) {
468 TelemetrySensor
* sensor
= & g_model
.telemetrySensors
[i
];
469 if (sensor
->isAvailable()) {
470 POPUP_MENU_ADD_ITEM(STR_MENU_TELEMETRY
);
475 POPUP_MENU_START(onSourceLongEnterPress
);
477 if (checkIncDecSelection
!= 0) {
478 newval
= checkIncDecSelection
;
479 if (checkIncDecSelection
!= MIXSRC_MAX
)
480 s_editMode
= EDIT_MODIFY_FIELD
;
481 checkIncDecSelection
= 0;
484 else if (i_flags
& INCDEC_SWITCH
) {
485 if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
487 checkIncDecSelection
= SWSRC_NONE
;
488 if (i_min
<= SWSRC_FIRST_SWITCH
&& i_max
>= SWSRC_LAST_SWITCH
) POPUP_MENU_ADD_ITEM(STR_MENU_SWITCHES
);
489 if (i_min
<= SWSRC_FIRST_TRIM
&& i_max
>= SWSRC_LAST_TRIM
) POPUP_MENU_ADD_ITEM(STR_MENU_TRIMS
);
490 if (i_min
<= SWSRC_FIRST_LOGICAL_SWITCH
&& i_max
>= SWSRC_LAST_LOGICAL_SWITCH
) {
491 for (int i
= 0; i
< MAX_LOGICAL_SWITCHES
; i
++) {
492 if (isValueAvailable
&& isValueAvailable(SWSRC_FIRST_LOGICAL_SWITCH
+i
)) {
493 POPUP_MENU_ADD_ITEM(STR_MENU_LOGICAL_SWITCHES
);
498 if (isValueAvailable
&& isValueAvailable(SWSRC_ON
)) POPUP_MENU_ADD_ITEM(STR_MENU_OTHER
);
499 if (isValueAvailable
&& isValueAvailable(-newval
)) POPUP_MENU_ADD_ITEM(STR_MENU_INVERT
);
500 POPUP_MENU_START(onSwitchLongEnterPress
);
501 s_editMode
= EDIT_MODIFY_FIELD
;
503 if (checkIncDecSelection
!= 0) {
504 newval
= (checkIncDecSelection
== SWSRC_INVERT
? -newval
: checkIncDecSelection
);
505 s_editMode
= EDIT_MODIFY_FIELD
;
506 checkIncDecSelection
= 0;
514 int16_t checkIncDec(event_t event
, int16_t val
, int16_t i_min
, int16_t i_max
, uint8_t i_flags
)
516 int16_t newval
= val
;
519 uint8_t in
= KEYS_PRESSED();
520 if (!(i_flags
& NO_DBLKEYS
) && (EVT_KEY_MASK(event
))) {
522 if (DBLKEYS_PRESSED_RGT_LFT(in
))
524 else if (DBLKEYS_PRESSED_RGT_UP(in
)) {
525 newval
= (i_max
> 100 ? 100 : i_max
);
527 else if (DBLKEYS_PRESSED_LFT_DWN(in
)) {
528 newval
= (i_min
< -100 ? -100 : i_min
);
530 else if (DBLKEYS_PRESSED_UP_DWN(in
)) {
539 killEvents(KEY_DOWN
);
540 killEvents(KEY_RIGHT
);
541 killEvents(KEY_LEFT
);
547 if (event
==EVT_KEY_FIRST(KEY_RIGHT
) || event
==EVT_KEY_REPT(KEY_RIGHT
) || (s_editMode
>0 && (IS_ROTARY_RIGHT(event
) || event
==EVT_KEY_FIRST(KEY_UP
) || event
==EVT_KEY_REPT(KEY_UP
)))) {
550 else if (event
==EVT_KEY_FIRST(KEY_LEFT
) || event
==EVT_KEY_REPT(KEY_LEFT
) || (s_editMode
>0 && (IS_ROTARY_LEFT(event
) || event
==EVT_KEY_FIRST(KEY_DOWN
) || event
==EVT_KEY_REPT(KEY_DOWN
)))) {
554 if (!READ_ONLY() && i_min
==0 && i_max
==1 && (event
==EVT_KEY_BREAK(KEY_ENTER
) || IS_ROTARY_BREAK(event
))) {
559 #if defined(NAVIGATION_POT1)
560 // change values based on P1
565 #if defined(AUTOSWITCH)
566 if (i_flags
& INCDEC_SWITCH
) {
567 newval
= checkIncDecMovedSwitch(newval
);
571 #if defined(AUTOSOURCE)
572 if (i_flags
& INCDEC_SOURCE
) {
574 int8_t source
= GET_MOVED_SOURCE(i_min
, i_max
);
578 #if defined(AUTOSWITCH)
580 uint8_t swtch
= abs(getMovedSwitch());
582 newval
= switchToMix(swtch
);
590 if (newval
> i_max
|| newval
< i_min
) {
591 newval
= (newval
> i_max
? i_max
: i_min
);
597 if (!(i_flags
& NO_INCDEC_MARKS
) && (newval
!= i_max
) && (newval
!= i_min
) && (newval
==0 || newval
==-100 || newval
==+100) && !IS_ROTARY_EVENT(event
)) {
598 pauseEvents(event
); // delay before auto-repeat continues
600 if (!IS_KEY_REPT(event
)) {
603 storageDirty(i_flags
& (EE_GENERAL
|EE_MODEL
));
604 checkIncDec_Ret
= (newval
> val
? 1 : -1);
614 int8_t checkIncDecModel(event_t event
, int8_t i_val
, int8_t i_min
, int8_t i_max
)
616 return checkIncDec(event
, i_val
, i_min
, i_max
, EE_MODEL
);
619 int8_t checkIncDecModelZero(event_t event
, int8_t i_val
, int8_t i_max
)
621 return checkIncDecModel(event
, i_val
, 0, i_max
);
624 int8_t checkIncDecGen(event_t event
, int8_t i_val
, int8_t i_min
, int8_t i_max
)
626 return checkIncDec(event
, i_val
, i_min
, i_max
, EE_GENERAL
);
631 #define SCROLL_POT1_TH 32
634 #define CURSOR_NOT_ALLOWED_IN_ROW(row) ((int8_t)MAXCOL(row) < 0)
636 #define CURSOR_NOT_ALLOWED_IN_ROW(row) (MAXCOL(row) == TITLE_ROW)
639 #define INC(val, min, max) if (val<max) {val++;} else {val=min;}
640 #define DEC(val, min, max) if (val>min) {val--;} else {val=max;}
643 tmr10ms_t menuEntryTime
;
647 #define MAXCOL_RAW(row) (horTab ? pgm_read_byte(horTab+min(row, (vertpos_t)horTabMax)) : (const uint8_t)0)
648 #define MAXCOL(row) (MAXCOL_RAW(row) >= HIDDEN_ROW ? MAXCOL_RAW(row) : (const uint8_t)(MAXCOL_RAW(row) & (~NAVIGATION_LINE_BY_LINE)))
649 #define COLATTR(row) (MAXCOL_RAW(row) == (uint8_t)-1 ? (const uint8_t)0 : (const uint8_t)(MAXCOL_RAW(row) & NAVIGATION_LINE_BY_LINE))
650 #define MENU_FIRST_LINE_EDIT (menuTab ? (MAXCOL((uint16_t)0) >= HIDDEN_ROW ? (MAXCOL((uint16_t)1) >= HIDDEN_ROW ? 2 : 1) : 0) : 0)
651 #define POS_HORZ_INIT(posVert) ((COLATTR(posVert) & NAVIGATION_LINE_BY_LINE) ? -1 : 0)
653 void check(event_t event
, uint8_t curr
, const MenuHandlerFunc
* menuTab
, uint8_t menuTabSize
, const pm_uint8_t
* horTab
, uint8_t horTabMax
, vertpos_t rowcount
)
655 vertpos_t l_posVert
= menuVerticalPosition
;
656 horzpos_t l_posHorz
= menuHorizontalPosition
;
658 uint8_t maxcol
= MAXCOL(l_posVert
);
664 case EVT_KEY_LONG(KEY_MENU
):
665 if (menuTab
== menuTabModel
) {
667 if (modelHasNotes()) {
668 POPUP_MENU_ADD_SD_ITEM(STR_VIEW_CHANNELS
);
669 POPUP_MENU_ADD_ITEM(STR_VIEW_NOTES
);
670 POPUP_MENU_START(onLongMenuPress
);
673 pushMenu(menuChannelsView
);
679 case EVT_KEY_LONG(KEY_PAGE
):
687 case EVT_KEY_BREAK(KEY_PAGE
):
688 if (curr
< (menuTabSize
-1))
695 if (!menuCalibrationState
&& cc
!= curr
) {
696 chainMenu((MenuHandlerFunc
)pgm_read_adr(&menuTab
[cc
]));
699 // TODO if (!(flags&CHECK_FLAG_NO_SCREEN_INDEX)) {
700 drawScreenIndex(curr
, menuTabSize
, 0);
703 // TODO lcdDrawFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, SOLID, FILL_WHITE|GREY_DEFAULT);
706 DISPLAY_PROGRESS_BAR(menuTab
? lcdLastRightPos
-2*FW
-((curr
+1)/10*FWNUM
)-2 : 20*FW
+1);
710 menuEntryTime
= get_tmr10ms();
711 s_editMode
= EDIT_MODE_INIT
;
712 l_posVert
= MENU_FIRST_LINE_EDIT
;
713 l_posHorz
= POS_HORZ_INIT(l_posVert
);
717 menuEntryTime
= get_tmr10ms();
719 l_posHorz
= POS_HORZ_INIT(l_posVert
);
722 case EVT_ROTARY_BREAK
:
723 if (s_editMode
> 1) break;
724 if (menuHorizontalPosition
< 0 && maxcol
> 0 && READ_ONLY_UNLOCKED()) {
728 else if (READ_ONLY_UNLOCKED()) {
729 s_editMode
= (s_editMode
<=0);
734 case EVT_KEY_LONG(KEY_EXIT
):
735 s_editMode
= 0; // TODO needed? we call ENTRY_UP after which does the same
739 case EVT_KEY_BREAK(KEY_EXIT
):
740 if (s_editMode
> 0) {
746 if (l_posHorz
>= 0 && (COLATTR(l_posVert
) & NAVIGATION_LINE_BY_LINE
)) {
751 uint8_t posVertInit
= MENU_FIRST_LINE_EDIT
;
752 if (menuVerticalOffset
!= 0 || l_posVert
!= posVertInit
) {
753 menuVerticalOffset
= 0;
754 l_posVert
= posVertInit
;
755 l_posHorz
= POS_HORZ_INIT(l_posVert
);
764 case EVT_ROTARY_RIGHT
:
765 case EVT_KEY_FIRST(KEY_RIGHT
):
768 case EVT_KEY_REPT(KEY_RIGHT
):
769 if (s_editMode
> 0) break; // TODO it was !=
770 if ((COLATTR(l_posVert
) & NAVIGATION_LINE_BY_LINE
)) {
771 if (l_posHorz
>= 0) {
772 INC(l_posHorz
, 0, maxcol
);
777 if (l_posHorz
< maxcol
) {
787 INC(l_posVert
, MENU_FIRST_LINE_EDIT
, rowcount
-1);
788 } while (CURSOR_NOT_ALLOWED_IN_ROW(l_posVert
));
790 s_editMode
= 0; // if we go down, we must be in this mode
792 l_posHorz
= POS_HORZ_INIT(l_posVert
);
795 case EVT_ROTARY_LEFT
:
796 case EVT_KEY_FIRST(KEY_LEFT
):
799 case EVT_KEY_REPT(KEY_LEFT
):
800 if (s_editMode
> 0) break; // TODO it was !=
801 if ((COLATTR(l_posVert
) & NAVIGATION_LINE_BY_LINE
)) {
802 if (l_posHorz
>= 0) {
803 DEC(l_posHorz
, 0, maxcol
);
807 else if (l_posHorz
> 0) {
816 DEC(l_posVert
, MENU_FIRST_LINE_EDIT
, rowcount
-1);
817 } while (CURSOR_NOT_ALLOWED_IN_ROW(l_posVert
));
819 s_editMode
= 0; // if we go up, we must be in this mode
821 if ((COLATTR(l_posVert
) & NAVIGATION_LINE_BY_LINE
))
824 l_posHorz
= min((uint8_t)l_posHorz
, MAXCOL(l_posVert
));
829 int linesCount
= rowcount
;
831 if (l_posVert
== 0 || (l_posVert
==1 && MAXCOL(vertpos_t(0)) >= HIDDEN_ROW
) || (l_posVert
==2 && MAXCOL(vertpos_t(0)) >= HIDDEN_ROW
&& MAXCOL(vertpos_t(1)) >= HIDDEN_ROW
)) {
832 menuVerticalOffset
= 0;
835 for (int i
=0; i
<rowcount
; i
++) {
836 if (i
>horTabMax
|| horTab
[i
] != HIDDEN_ROW
) {
843 if (rowcount
> NUM_BODY_LINES
) {
845 vertpos_t firstLine
= 0;
846 for (int numLines
=0; firstLine
<rowcount
&& numLines
<menuVerticalOffset
; firstLine
++) {
847 if (firstLine
>=horTabMax
|| horTab
[firstLine
] != HIDDEN_ROW
) {
851 if (l_posVert
< firstLine
) {
852 menuVerticalOffset
--;
855 vertpos_t lastLine
= firstLine
;
856 for (int numLines
=0; lastLine
<rowcount
&& numLines
<NUM_BODY_LINES
; lastLine
++) {
857 if (lastLine
>=horTabMax
|| horTab
[lastLine
] != HIDDEN_ROW
) {
861 if (l_posVert
>= lastLine
) {
862 menuVerticalOffset
++;
865 linesCount
= menuVerticalOffset
+ NUM_BODY_LINES
;
866 for (int i
=lastLine
; i
<rowcount
; i
++) {
867 if (i
>horTabMax
|| horTab
[i
] != HIDDEN_ROW
) {
878 if (l_posVert
>=NUM_BODY_LINES
+menuVerticalOffset
) {
879 menuVerticalOffset
= l_posVert
-NUM_BODY_LINES
+1;
881 else if (l_posVert
<menuVerticalOffset
) {
882 menuVerticalOffset
= l_posVert
;
890 menuVerticalPosition
= l_posVert
;
891 menuHorizontalPosition
= l_posHorz
;
894 #define MAXCOL(row) (horTab ? pgm_read_byte(horTab+min(row, (vertpos_t)horTabMax)) : (const uint8_t)0)
895 #define POS_HORZ_INIT(posVert) 0
897 void check(event_t event
, uint8_t curr
, const MenuHandlerFunc
*menuTab
, uint8_t menuTabSize
, const pm_uint8_t
*horTab
, uint8_t horTabMax
, vertpos_t maxrow
)
899 vertpos_t l_posVert
= menuVerticalPosition
;
900 horzpos_t l_posHorz
= menuHorizontalPosition
;
902 uint8_t maxcol
= MAXCOL(l_posVert
);
904 #if defined(NAVIGATION_POT1)
905 // check pot 1 - if changed -> scroll values
906 static int16_t p1val
;
907 static int16_t p1valprev
;
908 p1valdiff
= (p1val
-calibratedAnalogs
[CALIBRATED_POT1
]) / SCROLL_POT1_TH
;
910 p1valdiff
= (p1valprev
-calibratedAnalogs
[CALIBRATED_POT1
]) / 2;
911 p1val
= calibratedAnalogs
[CALIBRATED_POT1
];
913 p1valprev
= calibratedAnalogs
[CALIBRATED_POT1
];
916 #if defined(NAVIGATION_POT2)
917 // check pot 2 - if changed -> scroll menu
918 static int16_t p2valprev
;
919 p2valdiff
= (p2valprev
-calibratedAnalogs
[CALIBRATED_POT2
]) / SCROLL_TH
;
920 if (p2valdiff
) p2valprev
= calibratedAnalogs
[CALIBRATED_POT2
];
923 #if defined(NAVIGATION_POT3)
924 // check pot 3 if changed -> cursor down/up
925 static int16_t p3valprev
;
926 int8_t scrollUD
= (p3valprev
-calibratedAnalogs
[CALIBRATED_POT3
]) / SCROLL_TH
;
927 if (scrollUD
) p3valprev
= calibratedAnalogs
[CALIBRATED_POT3
];
932 if (p2valdiff
|| scrollUD
|| p1valdiff
) backlightOn(); // on keypress turn the light on
937 if (l_posVert
==0 && !menuCalibrationState
) {
943 cc
= limit((int8_t)0, (int8_t)(cc
- p2valdiff
), (int8_t)(menuTabSize
-1));
947 #if defined(ROTARY_ENCODER_NAVIGATION)
948 case EVT_ROTARY_BREAK
:
949 if (s_editMode
< 0 && maxrow
> 0) {
951 // TODO ? l_posVert = (horTab && horTab[1]==0xff) ? 2 : 1;
961 #if defined(ROTARY_ENCODER_NAVIGATION)
962 case EVT_ROTARY_LEFT
:
966 case EVT_KEY_FIRST(KEY_LEFT
):
973 #if defined(ROTARY_ENCODER_NAVIGATION)
974 case EVT_ROTARY_RIGHT
:
978 case EVT_KEY_FIRST(KEY_RIGHT
):
979 if (curr
< (menuTabSize
-1))
987 chainMenu((MenuHandlerFunc
)pgm_read_adr(&menuTab
[cc
]));
990 #if defined(ROTARY_ENCODER_NAVIGATION)
991 if (IS_ROTARY_ENCODER_NAVIGATION_ENABLE() && s_editMode
< 0)
996 menuCalibrationState
= 0;
997 drawScreenIndex(curr
, menuTabSize
, attr
);
1001 DISPLAY_PROGRESS_BAR(menuTab
? lcdLastRightPos
-2*FW
-((curr
+1)/10*FWNUM
)-2 : 20*FW
+1);
1003 if (s_editMode
<=0) {
1005 l_posVert
= limit((int8_t)0, (int8_t)(l_posVert
- scrollUD
), (int8_t)maxrow
);
1006 l_posHorz
= min((uint8_t)l_posHorz
, MAXCOL(l_posVert
));
1009 if (p2valdiff
&& l_posVert
>0) {
1010 l_posHorz
= limit((int8_t)0, (int8_t)((uint8_t)l_posHorz
- p2valdiff
), (int8_t)maxcol
);
1018 menuEntryTime
= get_tmr10ms();
1021 l_posHorz
= POS_HORZ_INIT(l_posVert
);
1022 #if defined(ROTARY_ENCODER_NAVIGATION)
1024 s_editMode
= EDIT_MODE_INIT
;
1029 s_editMode
= EDIT_MODE_INIT
;
1033 #if defined(ROTARY_ENCODER_NAVIGATION)
1038 case EVT_ROTARY_BREAK
:
1039 if (s_editMode
> 1) break;
1042 case EVT_KEY_FIRST(KEY_ENTER
):
1043 if (!menuTab
|| l_posVert
>0) {
1044 if (READ_ONLY_UNLOCKED()) {
1045 s_editMode
= (s_editMode
<=0);
1050 #if defined(ROTARY_ENCODER_NAVIGATION)
1051 case EVT_ROTARY_LONG
:
1052 if (s_editMode
> 1) break;
1054 if (l_posVert
!= 0) {
1056 s_editMode
= EDIT_MODE_INIT
;
1061 case EVT_KEY_LONG(KEY_EXIT
):
1062 s_editMode
= 0; // TODO needed? we call ENTRY_UP after which does the same
1066 case EVT_KEY_BREAK(KEY_EXIT
):
1068 #if defined(ROTARY_ENCODER_NAVIGATION)
1069 if (s_editMode
== 0)
1070 s_editMode
= EDIT_MODE_INIT
;
1078 if (l_posVert
==0 || !menuTab
) {
1079 popMenu(); // beeps itself
1087 case EVT_KEY_REPT(KEY_RIGHT
): //inc
1088 if (l_posHorz
==maxcol
) break;
1091 case EVT_KEY_FIRST(KEY_RIGHT
)://inc
1092 if (!horTab
|| s_editMode
>0) break;
1094 #if defined(ROTARY_ENCODER_NAVIGATION)
1095 CASE_EVT_ROTARY_RIGHT
1096 if (s_editMode
!= 0) break;
1097 if (l_posHorz
< maxcol
) {
1103 if (!IS_ROTARY_RIGHT(event
))
1107 INC(l_posHorz
, 0, maxcol
);
1112 case EVT_KEY_REPT(KEY_DOWN
):
1113 if (!IS_ROTARY_RIGHT(event
) && l_posVert
==maxrow
) break;
1116 case EVT_KEY_FIRST(KEY_DOWN
):
1117 if (s_editMode
>0) break;
1119 INC(l_posVert
, 0, maxrow
);
1120 } while (CURSOR_NOT_ALLOWED_IN_ROW(l_posVert
));
1122 #if defined(ROTARY_ENCODER_NAVIGATION)
1123 s_editMode
= 0; // if we go down, we must be in this mode
1126 l_posHorz
= min
<horzpos_t
>(l_posHorz
, MAXCOL(l_posVert
));
1130 case EVT_KEY_REPT(KEY_LEFT
): //dec
1131 if (l_posHorz
==0) break;
1134 case EVT_KEY_FIRST(KEY_LEFT
)://dec
1135 if (!horTab
|| s_editMode
>0) break;
1137 #if defined(ROTARY_ENCODER_NAVIGATION)
1138 CASE_EVT_ROTARY_LEFT
1139 if (s_editMode
!= 0) break;
1140 if (l_posHorz
> 0) {
1144 else if (IS_ROTARY_LEFT(event
) && s_editMode
== 0) {
1152 DEC(l_posHorz
, 0, maxcol
);
1157 case EVT_KEY_REPT(KEY_UP
):
1158 if (!IS_ROTARY_LEFT(event
) && l_posVert
==0) break;
1160 case EVT_KEY_FIRST(KEY_UP
):
1161 if (s_editMode
>0) break;
1164 DEC(l_posVert
, 0, maxrow
);
1165 } while (CURSOR_NOT_ALLOWED_IN_ROW(l_posVert
));
1167 #if defined(ROTARY_ENCODER_NAVIGATION)
1168 s_editMode
= 0; // if we go up, we must be in this mode
1171 l_posHorz
= min((uint8_t)l_posHorz
, MAXCOL(l_posVert
));
1176 uint8_t maxLines
= menuTab
? LCD_LINES
-1 : LCD_LINES
-2;
1179 int linesCount
= maxrow
;
1180 if (l_posVert
== 0 || (l_posVert
==1 && MAXCOL(vertpos_t(0)) >= HIDDEN_ROW
) || (l_posVert
==2 && MAXCOL(vertpos_t(0)) >= HIDDEN_ROW
&& MAXCOL(vertpos_t(1)) >= HIDDEN_ROW
)) {
1181 menuVerticalOffset
= 0;
1184 for (int i
=0; i
<maxrow
; i
++) {
1185 if (i
>=horTabMax
|| horTab
[i
] != HIDDEN_ROW
) {
1192 if (maxrow
> maxLines
) {
1194 vertpos_t firstLine
= 0;
1195 for (int numLines
=0; firstLine
<maxrow
&& numLines
<menuVerticalOffset
; firstLine
++) {
1196 if (firstLine
>=horTabMax
|| horTab
[firstLine
+1] != HIDDEN_ROW
) {
1200 if (l_posVert
<= firstLine
) {
1201 menuVerticalOffset
--;
1204 vertpos_t lastLine
= firstLine
;
1205 for (int numLines
=0; lastLine
<maxrow
&& numLines
<maxLines
; lastLine
++) {
1206 if (lastLine
>=horTabMax
|| horTab
[lastLine
+1] != HIDDEN_ROW
) {
1210 if (l_posVert
> lastLine
) {
1211 menuVerticalOffset
++;
1214 linesCount
= menuVerticalOffset
+ maxLines
;
1215 for (int i
=lastLine
; i
<maxrow
; i
++) {
1216 if (i
>=horTabMax
|| horTab
[i
] != HIDDEN_ROW
) {
1228 menuVerticalOffset
=0;
1232 if (l_posVert
>maxLines
+menuVerticalOffset
) {
1233 menuVerticalOffset
= l_posVert
-maxLines
;
1235 else if (l_posVert
<=menuVerticalOffset
) {
1236 menuVerticalOffset
= l_posVert
-1;
1240 menuVerticalPosition
= l_posVert
;
1241 menuHorizontalPosition
= l_posHorz
;
1242 #if !defined(CPUM64)
1244 if (menuVerticalOffset
> 0) {
1246 if (l_posVert
== menuVerticalOffset
&& CURSOR_NOT_ALLOWED_IN_ROW(l_posVert
)) {
1247 menuVerticalOffset
= l_posVert
-1;
1254 void check_simple(event_t event
, uint8_t curr
, const MenuHandlerFunc
* menuTab
, uint8_t menuTabSize
, vertpos_t maxrow
)
1256 check(event
, curr
, menuTab
, menuTabSize
, 0, 0, maxrow
);
1259 void check_submenu_simple(event_t event
, uint8_t maxrow
)
1261 check_simple(event
, 0, 0, 0, maxrow
);
1264 void repeatLastCursorMove(event_t event
)
1266 if (CURSOR_MOVED_LEFT(event
) || CURSOR_MOVED_RIGHT(event
)) {
1270 menuHorizontalPosition
= 0;