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.
24 void displayMixStatus(uint8_t channel
);
26 int getMixesLinesCount()
29 uint8_t count
= MAX_OUTPUT_CHANNELS
;
30 for (int i
=0; i
<MAX_MIXERS
; i
++) {
31 bool valid
= mixAddress(i
)->srcRaw
;
34 int ch
= mixAddress(i
)->destCh
;
45 uint8_t getMixesCount()
50 for (int i
=MAX_MIXERS
-1; i
>=0; i
--) {
51 ch
= mixAddress(i
)->srcRaw
;
59 bool reachMixesLimit()
61 if (getMixesCount() >= MAX_MIXERS
) {
62 POPUP_WARNING(STR_NOFREEMIXER
);
68 void deleteMix(uint8_t idx
)
70 pauseMixerCalculations();
71 MixData
* mix
= mixAddress(idx
);
72 memmove(mix
, mix
+1, (MAX_MIXERS
-(idx
+1))*sizeof(MixData
));
73 memclear(&g_model
.mixData
[MAX_MIXERS
-1], sizeof(MixData
));
74 resumeMixerCalculations();
75 storageDirty(EE_MODEL
);
78 void insertMix(uint8_t idx
)
80 pauseMixerCalculations();
81 MixData
* mix
= mixAddress(idx
);
82 memmove(mix
+1, mix
, (MAX_MIXERS
-(idx
+1))*sizeof(MixData
));
83 memclear(mix
, sizeof(MixData
));
84 mix
->destCh
= s_currCh
-1;
85 mix
->srcRaw
= s_currCh
;
86 if (!isSourceAvailable(mix
->srcRaw
)) {
87 mix
->srcRaw
= (s_currCh
> 4 ? MIXSRC_Rud
- 1 + s_currCh
: MIXSRC_Rud
- 1 + channelOrder(s_currCh
));
88 while (!isSourceAvailable(mix
->srcRaw
)) {
93 resumeMixerCalculations();
94 storageDirty(EE_MODEL
);
97 void copyMix(uint8_t idx
)
99 pauseMixerCalculations();
100 MixData
* mix
= mixAddress(idx
);
101 memmove(mix
+1, mix
, (MAX_MIXERS
-(idx
+1))*sizeof(MixData
));
102 resumeMixerCalculations();
103 storageDirty(EE_MODEL
);
106 bool swapMixes(uint8_t & idx
, uint8_t up
)
109 int8_t tgt_idx
= (up
? idx
-1 : idx
+1);
120 if (tgt_idx
== MAX_MIXERS
) {
121 if (x
->destCh
== MAX_OUTPUT_CHANNELS
-1)
127 y
= mixAddress(tgt_idx
);
128 uint8_t destCh
= x
->destCh
;
129 if(!y
->srcRaw
|| destCh
!= y
->destCh
) {
131 if (destCh
>0) x
->destCh
--;
135 if (destCh
<MAX_OUTPUT_CHANNELS
-1) x
->destCh
++;
141 pauseMixerCalculations();
142 memswap(x
, y
, sizeof(MixData
));
143 resumeMixerCalculations();
156 CASE_FLIGHT_MODES(MIX_FIELD_FLIGHT_MODE
)
161 MIX_FIELD_DELAY_DOWN
,
167 void gvarWeightItem(coord_t x
, coord_t y
, MixData
*md
, LcdFlags attr
, event_t event
)
169 u_int8int16_t weight
;
170 MD_WEIGHT_TO_UNION(md
, weight
);
171 weight
.word
= GVAR_MENU_ITEM(x
, y
, weight
.word
, GV_RANGELARGE_WEIGHT_NEG
, GV_RANGELARGE_WEIGHT
, attr
, 0, event
);
172 MD_UNION_TO_WEIGHT(weight
, md
);
175 bool menuModelMixOne(event_t event
)
177 MixData
* md2
= mixAddress(s_currIdx
) ;
179 SUBMENU_WITH_OPTIONS(STR_MIXES
, ICON_MODEL_MIXER
, MIX_FIELD_COUNT
, OPTION_MENU_NO_SCROLLBAR
, { 0, 0, 0, 0, 0, 1, CASE_FLIGHT_MODES((MAX_FLIGHT_MODES
-1) | NAVIGATION_LINE_BY_LINE
) 0 /*, ...*/ });
180 putsChn(50, 3+FH
, md2
->destCh
+1, MENU_TITLE_COLOR
);
181 displayMixStatus(md2
->destCh
);
183 // The separation line between 2 columns
184 lcdDrawSolidVerticalLine(MENU_COLUMN2_X
-10, DEFAULT_SCROLLBAR_Y
-FH
, DEFAULT_SCROLLBAR_H
+5, TEXT_COLOR
);
186 int8_t sub
= menuVerticalPosition
;
187 int8_t editMode
= s_editMode
;
189 for (int k
=0; k
<2*NUM_BODY_LINES
; k
++) {
191 if (k
> NUM_BODY_LINES
) {
192 y
= MENU_CONTENT_TOP
- FH
+ (k
-NUM_BODY_LINES
)*FH
;
195 y
= MENU_CONTENT_TOP
- FH
+ k
*FH
;
199 LcdFlags attr
= (sub
==i
? (editMode
>0 ? BLINK
|INVERS
: INVERS
) : 0);
202 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MIXNAME
);
203 editName(MIXES_2ND_COLUMN
, y
, md2
->name
, sizeof(md2
->name
), event
, attr
);
205 case MIX_FIELD_SOURCE
:
206 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_SOURCE
);
207 drawSource(MIXES_2ND_COLUMN
, y
, md2
->srcRaw
, attr
);
208 if (attr
) CHECK_INCDEC_MODELSOURCE(event
, md2
->srcRaw
, 1, MIXSRC_LAST
);
210 case MIX_FIELD_WEIGHT
:
211 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_WEIGHT
);
212 gvarWeightItem(MIXES_2ND_COLUMN
, y
, md2
, attr
|LEFT
, event
);
214 case MIX_FIELD_OFFSET
:
216 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_OFFSET
);
217 u_int8int16_t offset
;
218 MD_OFFSET_TO_UNION(md2
, offset
);
219 offset
.word
= GVAR_MENU_ITEM(MIXES_2ND_COLUMN
, y
, offset
.word
, GV_RANGELARGE_OFFSET_NEG
, GV_RANGELARGE_OFFSET
, attr
|LEFT
, 0, event
);
220 MD_UNION_TO_OFFSET(offset
, md2
);
222 drawOffsetBar(x
+MIXES_2ND_COLUMN
+22, y
, md2
);
227 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_TRIM
);
228 drawCheckBox(MIXES_2ND_COLUMN
, y
, !md2
->carryTrim
, attr
);
229 if (attr
) md2
->carryTrim
= !checkIncDecModel(event
, !md2
->carryTrim
, 0, 1);
231 case MIX_FIELD_CURVE
:
232 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_CURVE
);
233 editCurveRef(MIXES_2ND_COLUMN
, y
, md2
->curve
, event
, attr
);
235 #if defined(FLIGHT_MODES)
236 case MIX_FIELD_FLIGHT_MODE
:
237 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_FLMODE
);
238 md2
->flightModes
= editFlightModes(MIXES_2ND_COLUMN
, y
, event
, md2
->flightModes
, attr
);
241 case MIX_FIELD_SWITCH
:
242 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_SWITCH
);
243 md2
->swtch
= editSwitch(MIXES_2ND_COLUMN
, y
, md2
->swtch
, attr
, event
);
245 case MIX_FIELD_WARNING
:
246 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MIXWARNING
);
248 lcdDrawNumber(MIXES_2ND_COLUMN
, y
, md2
->mixWarn
, attr
|LEFT
);
251 lcdDrawText(MIXES_2ND_COLUMN
, y
, STR_OFF
, attr
);
254 CHECK_INCDEC_MODELVAR_ZERO(event
, md2
->mixWarn
, 3);
257 case MIX_FIELD_MLTPX
:
258 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MULTPX
);
259 md2
->mltpx
= editChoice(MIXES_2ND_COLUMN
, y
, STR_VMLTPX
, md2
->mltpx
, 0, 2, attr
, event
);
261 case MIX_FIELD_DELAY_UP
:
262 lcdDrawText(MENU_COLUMN2_X
+MENUS_MARGIN_LEFT
, y
, STR_DELAYUP
);
263 md2
->delayUp
= editDelay(MENU_COLUMN2_X
, y
, event
, attr
, md2
->delayUp
);
265 case MIX_FIELD_DELAY_DOWN
:
266 lcdDrawText(MENU_COLUMN2_X
+MENUS_MARGIN_LEFT
, y
, STR_DELAYDOWN
);
267 md2
->delayDown
= editDelay(MENU_COLUMN2_X
, y
, event
, attr
, md2
->delayDown
);
269 case MIX_FIELD_SLOW_UP
:
270 lcdDrawText(MENU_COLUMN2_X
+MENUS_MARGIN_LEFT
, y
, STR_SLOWUP
);
271 md2
->speedUp
= editDelay(MENU_COLUMN2_X
, y
, event
, attr
, md2
->speedUp
);
273 case MIX_FIELD_SLOW_DOWN
:
274 lcdDrawText(MENU_COLUMN2_X
+MENUS_MARGIN_LEFT
, y
, STR_SLOWDOWN
);
275 md2
->speedDown
= editDelay(MENU_COLUMN2_X
, y
, event
, attr
, md2
->speedDown
);
283 #define _STR_MAX(x) "/" #x
284 #define STR_MAX(x) _STR_MAX(x)
286 #define MIX_LINE_WEIGHT_POS 105
287 #define MIX_LINE_SRC_POS 120
288 #define MIX_LINE_CURVE_ICON 175
289 #define MIX_LINE_CURVE_POS 195
290 #define MIX_LINE_SWITCH_ICON 260
291 #define MIX_LINE_SWITCH_POS 280
292 #define MIX_LINE_DELAY_SLOW_POS 340
293 #define MIX_LINE_NAME_FM_ICON 370
294 #define MIX_LINE_NAME_FM_POS 390
295 #define MIX_LINE_SELECT_POS 50
296 #define MIX_LINE_SELECT_WIDTH (LCD_W-MIX_LINE_SELECT_POS-15)
297 #define MIX_STATUS_BAR_W 130
298 #define MIX_STATUS_BAR_H 13
299 #define MIX_STATUS_CHAN_BAR MENUS_MARGIN_LEFT + 45
300 #define MIX_STATUS_ICON_MIXER MIX_STATUS_CHAN_BAR + 140
301 #define MIX_STATUS_ICON_TO MIX_STATUS_ICON_MIXER + 20
302 #define MIX_STATUS_ICON_OUTPUT MIX_STATUS_ICON_TO + 35
303 #define MIX_STATUS_OUT_NAME MIX_STATUS_ICON_OUTPUT + 25
304 #define MIX_STATUS_OUT_BAR LCD_W - MENUS_MARGIN_LEFT - MIX_STATUS_BAR_W
306 void lineMixSurround(coord_t y
, LcdFlags flags
=CURVE_AXIS_COLOR
)
308 lcdDrawRect(MIX_LINE_SELECT_POS
, y
-INVERT_VERT_MARGIN
+1, MIX_LINE_SELECT_WIDTH
, INVERT_LINE_HEIGHT
, 1, s_copyMode
== COPY_MODE
? SOLID
: DOTTED
, flags
);
311 void onMixesMenu(const char * result
)
313 uint8_t chn
= mixAddress(s_currIdx
)->destCh
+ 1;
315 if (result
== STR_EDIT
) {
316 pushMenu(menuModelMixOne
);
318 else if (result
== STR_INSERT_BEFORE
|| result
== STR_INSERT_AFTER
) {
319 if (!reachMixesLimit()) {
321 if (result
== STR_INSERT_AFTER
) { s_currIdx
++; menuVerticalPosition
++; }
322 insertMix(s_currIdx
);
323 pushMenu(menuModelMixOne
);
326 else if (result
== STR_COPY
|| result
== STR_MOVE
) {
327 s_copyMode
= (result
== STR_COPY
? COPY_MODE
: MOVE_MODE
);
328 s_copySrcIdx
= s_currIdx
;
330 s_copySrcRow
= menuVerticalPosition
;
332 else if (result
== STR_DELETE
) {
333 deleteMix(s_currIdx
);
337 void displayMixInfos(coord_t y
, MixData
*md
)
339 if (md
->curve
.value
!= 0 ) lcd
->drawBitmap(MIX_LINE_CURVE_ICON
, y
+ 2, mixerSetupCurveBitmap
);
340 drawCurveRef(MIX_LINE_CURVE_POS
, y
, md
->curve
);
343 lcd
->drawBitmap(MIX_LINE_SWITCH_ICON
, y
+ 2, mixerSetupSwitchBitmap
);
344 drawSwitch(MIX_LINE_SWITCH_POS
, y
, md
->swtch
);
348 void displayMixSmallFlightModes(coord_t x
, coord_t y
, FlightModesType value
)
350 for (int i
=0; i
<MAX_FLIGHT_MODES
; i
++) {
353 if (value
& (1<<i
)) lcd
->drawFilledRect(x
, y
+2, 8, 12 , SOLID
, CURVE_AXIS_COLOR
);
354 lcdDrawText(x
, y
, s
, SMLSIZE
);
359 void displayMixLine(coord_t y
, MixData
*md
)
361 if (md
->name
[0] && md
->flightModes
) {
362 if (SLOW_BLINK_ON_PHASE
) {
363 lcd
->drawBitmap(MIX_LINE_NAME_FM_ICON
, y
+ 2, mixerSetupFlightmodeBitmap
);
364 displayMixSmallFlightModes(MIX_LINE_NAME_FM_POS
, y
+ 2, md
->flightModes
);
367 lcd
->drawBitmap(MIX_LINE_NAME_FM_ICON
, y
+ 2, mixerSetupLabelBitmap
);
368 lcdDrawSizedText(MIX_LINE_NAME_FM_POS
, y
, md
->name
, sizeof(md
->name
), ZCHAR
);
371 else if (md
->name
[0]) {
372 lcd
->drawBitmap(MIX_LINE_NAME_FM_ICON
, y
+ 2, mixerSetupLabelBitmap
);
373 lcdDrawSizedText(MIX_LINE_NAME_FM_POS
, y
, md
->name
, sizeof(md
->name
), ZCHAR
);
375 else if (md
->flightModes
) {
376 lcd
->drawBitmap(MIX_LINE_NAME_FM_ICON
, y
+ 2, mixerSetupFlightmodeBitmap
);
377 displayMixSmallFlightModes(MIX_LINE_NAME_FM_POS
, y
+ 2, md
->flightModes
);
379 displayMixInfos(y
, md
);
382 void displayMixStatus(uint8_t channel
)
384 lcdDrawNumber(MENUS_MARGIN_LEFT
, MENU_FOOTER_TOP
, channel
+ 1, MENU_TITLE_COLOR
, 0, "CH", NULL
);
385 drawSingleMixerBar(MIX_STATUS_CHAN_BAR
, MENU_FOOTER_TOP
+ 4, MIX_STATUS_BAR_W
, MIX_STATUS_BAR_H
, channel
);
387 lcd
->drawBitmap(MIX_STATUS_ICON_MIXER
, MENU_FOOTER_TOP
, mixerSetupMixerBitmap
);
388 lcd
->drawBitmap(MIX_STATUS_ICON_TO
, MENU_FOOTER_TOP
, mixerSetupToBitmap
);
389 lcd
->drawBitmap(MIX_STATUS_ICON_OUTPUT
, MENU_FOOTER_TOP
, mixerSetupOutputBitmap
);
391 if (g_model
.limitData
[channel
].name
[0] == '\0')
392 lcdDrawNumber(MIX_STATUS_OUT_NAME
, MENU_FOOTER_TOP
, channel
+ 1, MENU_TITLE_COLOR
, 0, "CH", NULL
);
394 lcdDrawSizedText(MIX_STATUS_OUT_NAME
, MENU_FOOTER_TOP
, g_model
.limitData
[channel
].name
, sizeof(g_model
.limitData
[channel
].name
), MENU_TITLE_COLOR
| LEFT
| ZCHAR
);
395 drawSingleOutputBar(MIX_STATUS_OUT_BAR
, MENU_FOOTER_TOP
+ 4, MIX_STATUS_BAR_W
, MIX_STATUS_BAR_H
, channel
);
398 bool menuModelMixAll(event_t event
)
400 const BitmapBuffer
* mpx_mode
[] = {
402 mixerSetupMultiBitmap
,
403 mixerSetupReplaceBitmap
407 uint8_t sub
= menuVerticalPosition
;
409 if (s_editMode
> 0) {
413 uint8_t chn
= mixAddress(s_currIdx
)->destCh
+ 1;
415 int linesCount
= getMixesLinesCount();
416 SIMPLE_MENU(STR_MIXES
, MODEL_ICONS
, menuTabModel
, MENU_MODEL_MIXES
, linesCount
);
424 case EVT_KEY_LONG(KEY_EXIT
):
425 if (s_copyMode
&& s_copyTgtOfs
== 0) {
426 deleteMix(s_currIdx
);
431 case EVT_KEY_BREAK(KEY_EXIT
):
434 // cancel the current copy / move operation
435 if (s_copyMode
== COPY_MODE
) {
436 deleteMix(s_currIdx
);
440 swapMixes(s_currIdx
, s_copyTgtOfs
> 0);
441 s_copyTgtOfs
+= (s_copyTgtOfs
< 0 ? +1 : -1);
442 } while (s_copyTgtOfs
!= 0);
443 storageDirty(EE_MODEL
);
445 menuVerticalPosition
= s_copySrcRow
;
452 case EVT_KEY_BREAK(KEY_ENTER
):
453 if ((!s_currCh
|| (s_copyMode
&& !s_copyTgtOfs
)) && !READ_ONLY()) {
454 s_copyMode
= (s_copyMode
== COPY_MODE
? MOVE_MODE
: COPY_MODE
);
455 s_copySrcIdx
= s_currIdx
;
462 case EVT_KEY_LONG(KEY_ENTER
):
472 pushMenu(menuModelMixOne
);
476 if (s_copyMode
) s_currCh
= 0;
478 if (reachMixesLimit()) break;
479 insertMix(s_currIdx
);
480 pushMenu(menuModelMixOne
);
487 POPUP_MENU_ADD_ITEM(STR_EDIT
);
488 POPUP_MENU_ADD_ITEM(STR_INSERT_BEFORE
);
489 POPUP_MENU_ADD_ITEM(STR_INSERT_AFTER
);
490 POPUP_MENU_ADD_ITEM(STR_COPY
);
491 POPUP_MENU_ADD_ITEM(STR_MOVE
);
492 POPUP_MENU_ADD_ITEM(STR_DELETE
);
493 POPUP_MENU_START(onMixesMenu
);
499 case EVT_ROTARY_RIGHT
:
500 case EVT_ROTARY_LEFT
:
502 uint8_t next_ofs
= (event
==EVT_ROTARY_LEFT
? s_copyTgtOfs
- 1 : s_copyTgtOfs
+ 1);
504 if (s_copyTgtOfs
==0 && s_copyMode
==COPY_MODE
) {
505 // insert a mix on the same channel (just above / just below)
506 if (reachMixesLimit()) break;
508 if (event
==EVT_ROTARY_RIGHT
) s_currIdx
++;
509 else if (sub
-menuVerticalOffset
>= 6) menuVerticalOffset
++;
511 else if (next_ofs
==0 && s_copyMode
==COPY_MODE
) {
513 deleteMix(s_currIdx
);
514 if (event
==EVT_ROTARY_LEFT
) s_currIdx
--;
517 // only swap the mix with its neighbor
518 if (!swapMixes(s_currIdx
, event
==EVT_ROTARY_LEFT
)) break;
519 storageDirty(EE_MODEL
);
522 s_copyTgtOfs
= next_ofs
;
528 strAppendUnsigned(strAppend(strAppendUnsigned(str
, getMixesCount()), "/"), MAX_MIXERS
, 2);
529 lcdDrawText(MENU_TITLE_NEXT_POS
, MENU_TITLE_TOP
+1, str
, HEADER_COLOR
);
531 sub
= menuVerticalPosition
;
536 for (int ch
=1; ch
<=MAX_OUTPUT_CHANNELS
; ch
++) {
538 coord_t y
= MENU_CONTENT_TOP
+ (cur
-menuVerticalOffset
)*FH
;
539 if (i
<MAX_MIXERS
&& (md
=mixAddress(i
))->srcRaw
&& md
->destCh
+1 == ch
) {
540 if (cur
-menuVerticalOffset
>= 0 && cur
-menuVerticalOffset
< NUM_BODY_LINES
) {
541 putsChn(MENUS_MARGIN_LEFT
, y
, ch
, 0); // show CHx
546 if (s_copyMode
== MOVE_MODE
&& cur
-menuVerticalOffset
>= 0 && cur
-menuVerticalOffset
< NUM_BODY_LINES
&& s_copySrcCh
== ch
&& s_copyTgtOfs
!= 0 && i
== (s_copySrcIdx
+ (s_copyTgtOfs
<0))) {
550 if (s_currIdx
== i
) {
551 sub
= menuVerticalPosition
= cur
;
555 else if (sub
== cur
) {
557 displayMixStatus(ch
- 1);
559 if (cur
-menuVerticalOffset
>= 0 && cur
-menuVerticalOffset
< NUM_BODY_LINES
) {
560 LcdFlags attr
= ((s_copyMode
|| sub
!= cur
) ? 0 : INVERS
);
563 lcd
->drawBitmap(10, y
, mpx_mode
[md
->mltpx
]);
566 drawSource(MIX_LINE_SRC_POS
, y
, md
->srcRaw
);
568 if (mixCnt
== 0 && md
->mltpx
== 1)
569 lcdDrawText(MIX_LINE_WEIGHT_POS
, y
, "MULT!", RIGHT
| attr
| (isMixActive(i
) ? BOLD
: 0));
571 gvarWeightItem(MIX_LINE_WEIGHT_POS
, y
, md
, RIGHT
| attr
| (isMixActive(i
) ? BOLD
: 0), event
);
573 displayMixLine(y
, md
);
575 BitmapBuffer
*delayslowbmp
[] = {mixerSetupSlowBitmap
, mixerSetupDelayBitmap
, mixerSetupDelaySlowBitmap
};
576 uint8_t delayslow
= 0;
577 if (md
->speedDown
|| md
->speedUp
)
579 if (md
->delayUp
|| md
->delayDown
)
582 lcd
->drawBitmap(MIX_LINE_DELAY_SLOW_POS
, y
+ 2, delayslowbmp
[delayslow
- 1]);
585 if ((s_copyMode
==COPY_MODE
|| s_copyTgtOfs
== 0) && s_copySrcCh
== ch
&& i
== (s_copySrcIdx
+ (s_copyTgtOfs
<0))) {
586 /* draw a border around the raw on selection mode (copy/move) */
590 /* invert the raw when it's the current one */
591 lineMixSurround(y
, ALARM_COLOR
);
595 cur
++; y
+=FH
; mixCnt
++; i
++; md
++;
596 } while (i
<MAX_MIXERS
&& md
->srcRaw
&& md
->destCh
+1 == ch
);
597 if (s_copyMode
== MOVE_MODE
&& cur
-menuVerticalOffset
>= 0 && cur
-menuVerticalOffset
< NUM_BODY_LINES
&& s_copySrcCh
== ch
&& i
== (s_copySrcIdx
+ (s_copyTgtOfs
<0))) {
611 if (cur
-menuVerticalOffset
>= 0 && cur
-menuVerticalOffset
< NUM_BODY_LINES
) {
612 putsChn(MENUS_MARGIN_LEFT
, y
, ch
, attr
); // show CHx
613 if (s_copyMode
== MOVE_MODE
&& s_copySrcCh
== ch
) {
621 if (sub
>= linesCount
-1) menuVerticalPosition
= linesCount
-1;