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 #define MODEL_SPECIAL_FUNC_1ST_COLUMN (4*FW+2)
24 #define MODEL_SPECIAL_FUNC_2ND_COLUMN (8*FW+2)
25 #define MODEL_SPECIAL_FUNC_3RD_COLUMN (21*FW)
26 #define MODEL_SPECIAL_FUNC_4TH_COLUMN (33*FW-3)
27 #define MODEL_SPECIAL_FUNC_4TH_COLUMN_ONOFF (34*FW-3)
29 void onCustomFunctionsFileSelectionMenu(const char * result
)
31 int sub
= menuVerticalPosition
;
32 CustomFunctionData
* cfn
;
35 if (menuHandlers
[menuLevel
] == menuModelSpecialFunctions
) {
36 cfn
= &g_model
.customFn
[sub
];
40 cfn
= &g_eeGeneral
.customFn
[sub
];
44 uint8_t func
= CFN_FUNC(cfn
);
46 if (result
== STR_UPDATE_LIST
) {
48 if (func
== FUNC_PLAY_SCRIPT
) {
49 strcpy(directory
, SCRIPTS_FUNCS_PATH
);
52 strcpy(directory
, SOUNDS_PATH
);
53 strncpy(directory
+SOUNDS_PATH_LNG_OFS
, currentLanguagePack
->id
, 2);
55 if (!sdListFiles(directory
, func
==FUNC_PLAY_SCRIPT
? SCRIPTS_EXT
: SOUNDS_EXT
, sizeof(cfn
->play
.name
), NULL
)) {
56 POPUP_WARNING(func
==FUNC_PLAY_SCRIPT
? STR_NO_SCRIPTS_ON_SD
: STR_NO_SOUNDS_ON_SD
);
60 // The user choosed a file in the list
61 memcpy(cfn
->play
.name
, result
, sizeof(cfn
->play
.name
));
62 storageDirty(eeFlags
);
63 if (func
== FUNC_PLAY_SCRIPT
) {
64 LUA_LOAD_MODEL_SCRIPTS();
69 void onCustomFunctionsMenu(const char * result
)
71 int sub
= menuVerticalPosition
;
72 CustomFunctionData
* cfn
;
75 if (menuHandlers
[menuLevel
] == menuModelSpecialFunctions
) {
76 cfn
= &g_model
.customFn
[sub
];
80 cfn
= &g_eeGeneral
.customFn
[sub
];
84 if (result
== STR_COPY
) {
85 clipboard
.type
= CLIPBOARD_TYPE_CUSTOM_FUNCTION
;
86 clipboard
.data
.cfn
= *cfn
;
88 else if (result
== STR_PASTE
) {
89 *cfn
= clipboard
.data
.cfn
;
90 storageDirty(eeFlags
);
92 else if (result
== STR_CLEAR
) {
93 memset(cfn
, 0, sizeof(CustomFunctionData
));
94 storageDirty(eeFlags
);
96 else if (result
== STR_INSERT
) {
97 memmove(cfn
+1, cfn
, (MAX_SPECIAL_FUNCTIONS
-sub
-1)*sizeof(CustomFunctionData
));
98 memset(cfn
, 0, sizeof(CustomFunctionData
));
99 storageDirty(eeFlags
);
101 else if (result
== STR_DELETE
) {
102 memmove(cfn
, cfn
+1, (MAX_SPECIAL_FUNCTIONS
-sub
-1)*sizeof(CustomFunctionData
));
103 memset(&g_model
.customFn
[MAX_SPECIAL_FUNCTIONS
-1], 0, sizeof(CustomFunctionData
));
104 storageDirty(eeFlags
);
108 void onAdjustGvarSourceLongEnterPress(const char * result
)
110 CustomFunctionData
* cfn
= &g_model
.customFn
[menuVerticalPosition
];
112 if (result
== STR_CONSTANT
) {
113 CFN_GVAR_MODE(cfn
) = FUNC_ADJUST_GVAR_CONSTANT
;
115 storageDirty(EE_MODEL
);
117 else if (result
== STR_MIXSOURCE
) {
118 CFN_GVAR_MODE(cfn
) = FUNC_ADJUST_GVAR_SOURCE
;
120 storageDirty(EE_MODEL
);
122 else if (result
== STR_GLOBALVAR
) {
123 CFN_GVAR_MODE(cfn
) = FUNC_ADJUST_GVAR_GVAR
;
125 storageDirty(EE_MODEL
);
127 else if (result
== STR_INCDEC
) {
128 CFN_GVAR_MODE(cfn
) = FUNC_ADJUST_GVAR_INCDEC
;
130 storageDirty(EE_MODEL
);
133 onSourceLongEnterPress(result
);
137 enum CustomFunctionsItems
{
138 ITEM_CUSTOM_FUNCTIONS_SWITCH
,
139 ITEM_CUSTOM_FUNCTIONS_FUNCTION
,
140 ITEM_CUSTOM_FUNCTIONS_PARAM1
,
141 ITEM_CUSTOM_FUNCTIONS_PARAM2
,
142 ITEM_CUSTOM_FUNCTIONS_REPEAT
,
143 ITEM_CUSTOM_FUNCTIONS_COUNT
,
144 ITEM_CUSTOM_FUNCTIONS_LAST
= ITEM_CUSTOM_FUNCTIONS_COUNT
-1
147 void menuSpecialFunctions(event_t event
, CustomFunctionData
* functions
, CustomFunctionsContext
* functionsContext
)
149 int sub
= menuVerticalPosition
;
150 uint8_t eeFlags
= (functions
== g_model
.customFn
) ? EE_MODEL
: EE_GENERAL
;
151 if (menuHorizontalPosition
<0 && event
==EVT_KEY_LONG(KEY_ENTER
) && !READ_ONLY()) {
153 CustomFunctionData
*cfn
= &functions
[sub
];
155 POPUP_MENU_ADD_ITEM(STR_COPY
);
156 if (clipboard
.type
== CLIPBOARD_TYPE_CUSTOM_FUNCTION
)
157 POPUP_MENU_ADD_ITEM(STR_PASTE
);
158 if (!CFN_EMPTY(cfn
) && CFN_EMPTY(&functions
[MAX_SPECIAL_FUNCTIONS
-1]))
159 POPUP_MENU_ADD_ITEM(STR_INSERT
);
161 POPUP_MENU_ADD_ITEM(STR_CLEAR
);
162 for (int i
=sub
+1; i
<MAX_SPECIAL_FUNCTIONS
; i
++) {
163 if (!CFN_EMPTY(&functions
[i
])) {
164 POPUP_MENU_ADD_ITEM(STR_DELETE
);
168 POPUP_MENU_START(onCustomFunctionsMenu
);
171 for (int i
=0; i
<NUM_BODY_LINES
; i
++) {
172 coord_t y
= MENU_HEADER_HEIGHT
+ 1 + i
*FH
;
173 int k
= i
+menuVerticalOffset
;
175 drawStringWithIndex(0, y
, functions
== g_model
.customFn
? STR_SF
: STR_GF
, k
+1, (sub
==k
&& menuHorizontalPosition
<0) ? INVERS
: 0);
177 CustomFunctionData
* cfn
= &functions
[k
];
178 uint8_t func
= CFN_FUNC(cfn
);
179 for (uint8_t j
=0; j
<5; j
++) {
180 uint8_t attr
= ((sub
==k
&& menuHorizontalPosition
==j
) ? ((s_editMode
>0) ? BLINK
|INVERS
: INVERS
) : 0);
181 uint8_t active
= (attr
&& s_editMode
>0);
183 case ITEM_CUSTOM_FUNCTIONS_SWITCH
:
184 drawSwitch(MODEL_SPECIAL_FUNC_1ST_COLUMN
, y
, CFN_SWITCH(cfn
), attr
| ((functionsContext
->activeSwitches
& ((MASK_CFN_TYPE
)1 << k
)) ? BOLD
: 0));
185 if (active
|| AUTOSWITCH_ENTER_LONG()) CHECK_INCDEC_SWITCH(event
, CFN_SWITCH(cfn
), SWSRC_FIRST
, SWSRC_LAST
, eeFlags
, isSwitchAvailableInCustomFunctions
);
186 if (func
== FUNC_OVERRIDE_CHANNEL
&& functions
!= g_model
.customFn
) {
187 func
= CFN_FUNC(cfn
) = func
+1;
191 case ITEM_CUSTOM_FUNCTIONS_FUNCTION
:
192 if (CFN_SWITCH(cfn
)) {
193 lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_2ND_COLUMN
, y
, STR_VFSWFUNC
, func
, attr
);
195 func
= CFN_FUNC(cfn
) = checkIncDec(event
, CFN_FUNC(cfn
), 0, FUNC_MAX
-1, eeFlags
, isAssignableFunctionAvailable
);
196 if (checkIncDec_Ret
) CFN_RESET(cfn
);
200 j
= ITEM_CUSTOM_FUNCTIONS_LAST
; // skip other fields
201 if (sub
==k
&& menuHorizontalPosition
> 0) {
202 REPEAT_LAST_CURSOR_MOVE();
207 case ITEM_CUSTOM_FUNCTIONS_PARAM1
:
209 int8_t maxParam
= MAX_OUTPUT_CHANNELS
-1;
210 #if defined(OVERRIDE_CHANNEL_FUNCTION)
211 if (func
== FUNC_OVERRIDE_CHANNEL
) {
212 putsChn(lcdNextPos
, y
, CFN_CH_INDEX(cfn
)+1, attr
);
216 if (func
== FUNC_TRAINER
) {
217 maxParam
= NUM_STICKS
;
218 drawSource(lcdNextPos
, y
, CFN_CH_INDEX(cfn
)==0 ? 0 : MIXSRC_Rud
+CFN_CH_INDEX(cfn
)-1, attr
);
221 else if (func
== FUNC_ADJUST_GVAR
) {
222 maxParam
= MAX_GVARS
-1;
223 drawStringWithIndex(lcdNextPos
, y
, STR_GV
, CFN_GVAR_INDEX(cfn
)+1, attr
);
224 if (active
) CFN_GVAR_INDEX(cfn
) = checkIncDec(event
, CFN_GVAR_INDEX(cfn
), 0, maxParam
, eeFlags
);
228 else if (func
== FUNC_SET_TIMER
) {
230 drawStringWithIndex(lcdNextPos
, y
, STR_TIMER
, CFN_TIMER_INDEX(cfn
)+1, attr
);
231 if (active
) CFN_TIMER_INDEX(cfn
) = checkIncDec(event
, CFN_TIMER_INDEX(cfn
), 0, maxParam
, eeFlags
);
235 REPEAT_LAST_CURSOR_MOVE();
237 if (active
) CHECK_INCDEC_MODELVAR_ZERO(event
, CFN_CH_INDEX(cfn
), maxParam
);
241 case ITEM_CUSTOM_FUNCTIONS_PARAM2
:
243 INCDEC_DECLARE_VARS(eeFlags
);
244 int16_t val_displayed
= CFN_PARAM(cfn
);
246 int16_t val_max
= 255;
247 if (func
== FUNC_RESET
) {
248 val_max
= FUNC_RESET_PARAM_FIRST_TELEM
+lastUsedTelemetryIndex();
249 int param
= CFN_PARAM(cfn
);
250 if (param
< FUNC_RESET_PARAM_FIRST_TELEM
) {
251 lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, STR_VFSWRESET
, param
, attr
);
254 TelemetrySensor
* sensor
= & g_model
.telemetrySensors
[param
-FUNC_RESET_PARAM_FIRST_TELEM
];
255 lcdDrawSizedText(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, sensor
->label
, TELEM_LABEL_LEN
, attr
|ZCHAR
);
257 if (active
) INCDEC_ENABLE_CHECK(functionsContext
== &globalFunctionsContext
? isSourceAvailableInGlobalResetSpecialFunction
: isSourceAvailableInResetSpecialFunction
);
259 #if defined(OVERRIDE_CHANNEL_FUNCTION)
260 else if (func
== FUNC_OVERRIDE_CHANNEL
) {
261 val_min
= -LIMIT_EXT_PERCENT
; val_max
= +LIMIT_EXT_PERCENT
;
262 lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, val_displayed
, attr
|LEFT
);
265 else if (func
>= FUNC_SET_FAILSAFE
&& func
<= FUNC_BIND
) {
266 val_max
= NUM_MODULES
-1;
267 lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, "\004Int.Ext.", CFN_PARAM(cfn
), attr
);
269 else if (func
== FUNC_SET_TIMER
) {
270 val_max
= 539*60+59; //8:59:59
271 drawTimer(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, val_displayed
, attr
|LEFT
|TIMEHOUR
, attr
);
273 else if (func
== FUNC_PLAY_SOUND
) {
274 val_max
= AU_SPECIAL_SOUND_LAST
-AU_SPECIAL_SOUND_FIRST
-1;
275 lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, STR_FUNCSOUNDS
, val_displayed
, attr
);
278 else if (func
== FUNC_HAPTIC
) {
280 lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, val_displayed
, attr
|LEFT
);
284 else if (func
== FUNC_PLAY_TRACK
|| func
== FUNC_BACKGND_MUSIC
|| func
== FUNC_PLAY_SCRIPT
) {
285 coord_t x
= MODEL_SPECIAL_FUNC_3RD_COLUMN
;
286 if (ZEXIST(cfn
->play
.name
))
287 lcdDrawSizedText(x
, y
, cfn
->play
.name
, sizeof(cfn
->play
.name
), attr
);
289 lcdDrawTextAtIndex(x
, y
, STR_VCSWFUNC
, 0, attr
);
290 if (active
&& event
==EVT_KEY_BREAK(KEY_ENTER
)) {
293 if (func
==FUNC_PLAY_SCRIPT
) {
294 strcpy(directory
, SCRIPTS_FUNCS_PATH
);
297 strcpy(directory
, SOUNDS_PATH
);
298 strncpy(directory
+SOUNDS_PATH_LNG_OFS
, currentLanguagePack
->id
, 2);
300 if (sdListFiles(directory
, func
==FUNC_PLAY_SCRIPT
? SCRIPTS_EXT
: SOUNDS_EXT
, sizeof(cfn
->play
.name
), cfn
->play
.name
)) {
301 POPUP_MENU_START(onCustomFunctionsFileSelectionMenu
);
304 POPUP_WARNING(func
==FUNC_PLAY_SCRIPT
? STR_NO_SCRIPTS_ON_SD
: STR_NO_SOUNDS_ON_SD
);
309 else if (func
== FUNC_PLAY_VALUE
) {
310 val_max
= MIXSRC_LAST_TELEM
;
311 drawSource(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, val_displayed
, attr
);
313 INCDEC_SET_FLAG(eeFlags
| INCDEC_SOURCE
);
314 INCDEC_ENABLE_CHECK(functionsContext
== &globalFunctionsContext
? isSourceAvailableInGlobalFunctions
: isSourceAvailable
);
318 else if (func
== FUNC_VOLUME
) {
319 val_max
= MIXSRC_LAST_CH
;
320 drawSource(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, val_displayed
, attr
);
322 INCDEC_SET_FLAG(eeFlags
| INCDEC_SOURCE
);
323 INCDEC_ENABLE_CHECK(isSourceAvailable
);
326 else if (func
== FUNC_LOGS
) {
328 lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, val_displayed
, attr
|PREC1
|LEFT
);
329 lcdDrawChar(lcdLastRightPos
, y
, 's');
332 lcdDrawMMM(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, attr
);
335 #if defined(PCBX9E) || defined(PCBX9DP)
336 else if (func
== FUNC_BACKLIGHT
) {
337 drawSlider(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, CFN_PARAM(cfn
), 100, attr
);
338 if (active
) INCDEC_SET_FLAG(eeFlags
| NO_INCDEC_MARKS
);
344 else if (func
== FUNC_ADJUST_GVAR
) {
345 switch (CFN_GVAR_MODE(cfn
)) {
346 case FUNC_ADJUST_GVAR_CONSTANT
:
348 uint8_t gvar_index
= CFN_GVAR_INDEX(cfn
);
349 val_displayed
= (int16_t)CFN_PARAM(cfn
);
350 val_min
= max
<int16_t>(CFN_GVAR_CST_MIN
, MODEL_GVAR_MIN(gvar_index
));
351 val_max
= min
<int16_t>(CFN_GVAR_CST_MAX
, MODEL_GVAR_MAX(gvar_index
));
352 drawGVarValue(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, gvar_index
, val_displayed
, attr
|LEFT
);
355 case FUNC_ADJUST_GVAR_SOURCE
:
356 val_max
= MIXSRC_LAST_CH
;
357 drawSource(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, val_displayed
, attr
);
359 INCDEC_SET_FLAG(eeFlags
| INCDEC_SOURCE
);
360 INCDEC_ENABLE_CHECK(isSourceAvailable
);
363 case FUNC_ADJUST_GVAR_GVAR
:
364 val_max
= MAX_GVARS
-1;
365 drawStringWithIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, STR_GV
, val_displayed
+1, attr
);
367 default: // FUNC_ADJUST_GVAR_INC
368 val_min
= -100; val_max
= +100;
369 if (val_displayed
< 0)
370 lcdDrawText(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, "-= ", attr
);
372 lcdDrawText(MODEL_SPECIAL_FUNC_3RD_COLUMN
, y
, "+= ", attr
);
373 drawGVarValue(lcdNextPos
, y
, CFN_GVAR_INDEX(cfn
), abs(val_displayed
), attr
|LEFT
);
378 REPEAT_LAST_CURSOR_MOVE();
381 if (active
|| event
==EVT_KEY_LONG(KEY_ENTER
)) {
382 CFN_PARAM(cfn
) = CHECK_INCDEC_PARAM(event
, val_displayed
, val_min
, val_max
);
383 if (func
== FUNC_ADJUST_GVAR
&& attr
&& event
==EVT_KEY_LONG(KEY_ENTER
)) {
385 if (CFN_GVAR_MODE(cfn
) != FUNC_ADJUST_GVAR_CONSTANT
)
386 POPUP_MENU_ADD_ITEM(STR_CONSTANT
);
387 if (CFN_GVAR_MODE(cfn
) != FUNC_ADJUST_GVAR_SOURCE
)
388 POPUP_MENU_ADD_ITEM(STR_MIXSOURCE
);
389 if (CFN_GVAR_MODE(cfn
) != FUNC_ADJUST_GVAR_GVAR
)
390 POPUP_MENU_ADD_ITEM(STR_GLOBALVAR
);
391 if (CFN_GVAR_MODE(cfn
) != FUNC_ADJUST_GVAR_INCDEC
)
392 POPUP_MENU_ADD_ITEM(STR_INCDEC
);
393 POPUP_MENU_START(onAdjustGvarSourceLongEnterPress
);
394 s_editMode
= EDIT_MODIFY_FIELD
;
400 case ITEM_CUSTOM_FUNCTIONS_REPEAT
:
401 if (HAS_ENABLE_PARAM(func
)) {
402 drawCheckBox(MODEL_SPECIAL_FUNC_4TH_COLUMN_ONOFF
, y
, CFN_ACTIVE(cfn
), attr
);
403 if (active
) CFN_ACTIVE(cfn
) = checkIncDec(event
, CFN_ACTIVE(cfn
), 0, 1, eeFlags
);
405 else if (HAS_REPEAT_PARAM(func
)) {
406 if (CFN_PLAY_REPEAT(cfn
) == 0) {
407 lcdDrawText(MODEL_SPECIAL_FUNC_4TH_COLUMN
+2, y
, "1x", attr
);
409 else if (CFN_PLAY_REPEAT(cfn
) == CFN_PLAY_REPEAT_NOSTART
) {
410 lcdDrawChar(MODEL_SPECIAL_FUNC_4TH_COLUMN
-1, y
, '!', attr
);
411 lcdDrawText(MODEL_SPECIAL_FUNC_4TH_COLUMN
+2, y
, "1x", attr
);
414 lcdDrawNumber(MODEL_SPECIAL_FUNC_4TH_COLUMN
+2+FW
, y
, CFN_PLAY_REPEAT(cfn
)*CFN_PLAY_REPEAT_MUL
, attr
|RIGHT
);
415 lcdDrawChar(MODEL_SPECIAL_FUNC_4TH_COLUMN
+2+FW
, y
, 's', attr
);
417 if (active
) CFN_PLAY_REPEAT(cfn
) = checkIncDec(event
, CFN_PLAY_REPEAT(cfn
)==CFN_PLAY_REPEAT_NOSTART
?-1:CFN_PLAY_REPEAT(cfn
), -1, 60/CFN_PLAY_REPEAT_MUL
, eeFlags
);
420 REPEAT_LAST_CURSOR_MOVE();
428 void menuModelSpecialFunctions(event_t event
)
430 MENU(STR_MENUCUSTOMFUNC
, menuTabModel
, MENU_MODEL_SPECIAL_FUNCTIONS
, MAX_SPECIAL_FUNCTIONS
, { NAVIGATION_LINE_BY_LINE
|4/*repeated*/ });
431 return menuSpecialFunctions(event
, g_model
.customFn
, &modelFunctionsContext
);