Bubbles: Don't save scores when quit without saving is selected and reduce splash...
[kugel-rb.git] / apps / recorder / keyboard.c
blob76e7df0509e39c36b733b98130ad1cdd82398306
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Björn Stenberg
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "kernel.h"
22 #include "system.h"
23 #include <string.h>
24 #include "font.h"
25 #include "screens.h"
26 #include "talk.h"
27 #include "settings.h"
28 #include "misc.h"
29 #include "rbunicode.h"
30 #include "buttonbar.h"
31 #include "logf.h"
32 #include "hangul.h"
33 #include "action.h"
34 #include "icon.h"
35 #include "pcmbuf.h"
36 #include "lang.h"
37 #include "keyboard.h"
38 #include "viewport.h"
39 #include "file.h"
40 #include "splash.h"
42 #ifndef O_BINARY
43 #define O_BINARY 0
44 #endif
47 #define DEFAULT_MARGIN 6
48 #define KBD_BUF_SIZE 500
50 #if (CONFIG_KEYPAD == ONDIO_PAD) \
51 || (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
52 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
53 || (CONFIG_KEYPAD == IPOD_4G_PAD) \
54 || (CONFIG_KEYPAD == IRIVER_IFP7XX_PAD) \
55 || (CONFIG_KEYPAD == IAUDIO_X5M5_PAD) \
56 || (CONFIG_KEYPAD == IAUDIO_M3_PAD) \
57 || (CONFIG_KEYPAD == IRIVER_H10_PAD)
58 /* no key combos to move the cursor if not in line edit mode */
59 #define KBD_MODES /* uses 2 modes, picker and line edit */
61 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) \
62 || (CONFIG_KEYPAD == IRIVER_H300_PAD) \
63 || (CONFIG_KEYPAD == GIGABEAT_PAD) \
64 || (CONFIG_KEYPAD == GIGABEAT_S_PAD) \
65 || (CONFIG_KEYPAD == SANSA_E200_PAD) \
66 || (CONFIG_KEYPAD == SANSA_FUZE_PAD) \
67 || (CONFIG_KEYPAD == SANSA_C200_PAD) \
68 || (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
69 /* certain key combos move the cursor even if not in line edit mode */
70 #define KBD_CURSOR_KEYS
71 #define KBD_MODES /* uses 2 modes, picker and line edit */
73 #else
74 #define KBD_CURSOR_KEYS /* certain keys move the cursor, no line edit mode */
75 #endif
77 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) \
78 || (CONFIG_KEYPAD == IRIVER_H300_PAD) \
79 || (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
80 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
81 || (CONFIG_KEYPAD == IPOD_4G_PAD) \
82 || (CONFIG_KEYPAD == IRIVER_H10_PAD) \
83 || (CONFIG_KEYPAD == GIGABEAT_PAD) \
84 || (CONFIG_KEYPAD == GIGABEAT_S_PAD) \
85 || (CONFIG_KEYPAD == MROBE100_PAD) \
86 || (CONFIG_KEYPAD == SANSA_E200_PAD) \
87 || (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD) \
88 || (CONFIG_KEYPAD == PHILIPS_SA9200_PAD)
89 /* certain key combos toggle input mode between keyboard input and Morse input */
90 #define KBD_TOGGLE_INPUT
91 #endif
93 struct keyboard_parameters
95 unsigned short kbd_buf[KBD_BUF_SIZE];
96 int default_lines;
97 int nchars;
98 int font_w;
99 int font_h;
100 int text_w;
101 int curfont;
102 int main_y;
103 #ifdef HAVE_MORSE_INPUT
104 int old_main_y;
105 #endif
106 int max_chars;
107 int max_chars_text;
108 int lines;
109 int pages;
110 int keyboard_margin;
111 int curpos;
112 int leftpos;
113 int page;
114 int x;
115 int y;
116 #ifdef KBD_MODES
117 bool line_edit;
118 #endif
121 struct edit_state
123 char* text;
124 int buflen;
125 int len_utf8;
126 int editpos; /* Edit position on all screens */
127 bool cur_blink; /* Cursor on/off flag */
128 bool hangul;
129 unsigned short hlead, hvowel, htail;
130 #ifdef HAVE_MORSE_INPUT
131 bool morse_mode;
132 bool morse_reading;
133 unsigned char morse_code;
134 int morse_tick;
135 #endif
138 static struct keyboard_parameters kbd_param[NB_SCREENS];
139 static bool kbd_loaded = false;
141 #ifdef HAVE_MORSE_INPUT
142 /* FIXME: We should put this to a configuration file. */
143 static const char *morse_alphabets =
144 "abcdefghijklmnopqrstuvwxyz1234567890,.?-@ ";
145 static const unsigned char morse_codes[] = {
146 0x05,0x18,0x1a,0x0c,0x02,0x12,0x0e,0x10,0x04,0x17,0x0d,0x14,0x07,
147 0x06,0x0f,0x16,0x1d,0x0a,0x08,0x03,0x09,0x11,0x0b,0x19,0x1b,0x1c,
148 0x2f,0x27,0x23,0x21,0x20,0x30,0x38,0x3c,0x3e,0x3f,
149 0x73,0x55,0x4c,0x61,0x5a,0x80 };
150 #endif
152 /* Loads a custom keyboard into memory
153 call with NULL to reset keyboard */
154 int load_kbd(unsigned char* filename)
156 int fd, l;
157 int i = 0;
158 unsigned char buf[4];
160 if (filename == NULL)
162 kbd_loaded = false;
163 return 0;
166 fd = open_utf8(filename, O_RDONLY|O_BINARY);
167 if (fd < 0)
168 return 1;
170 while (read(fd, buf, 1) == 1 && i < KBD_BUF_SIZE)
172 /* check how many bytes to read for this character */
173 static const unsigned char sizes[4] = { 0x80, 0xe0, 0xf0, 0xf5 };
174 size_t count;
175 unsigned short ch;
177 for (count = 0; count < ARRAYLEN(sizes); count++)
179 if (buf[0] < sizes[count])
180 break;
183 if (count >= ARRAYLEN(sizes))
184 continue; /* Invalid size. */
186 if (read(fd, &buf[1], count) != (ssize_t)count)
188 close(fd);
189 kbd_loaded = false;
190 return 1;
193 utf8decode(buf, &ch);
194 if (ch != 0xFEFF && ch != '\r') /* skip BOM & carriage returns */
196 FOR_NB_SCREENS(l)
197 kbd_param[l].kbd_buf[i] = ch;
198 i++;
202 close(fd);
203 kbd_loaded = true;
205 FOR_NB_SCREENS(l)
207 struct keyboard_parameters *pm = &kbd_param[l];
208 pm->nchars = i;
209 /* initialize parameters */
210 pm->x = pm->y = pm->page = 0;
211 pm->default_lines = 0;
214 return 0;
217 /* helper function to spell a char if voice UI is enabled */
218 static void kbd_spellchar(unsigned short c)
220 if (global_settings.talk_menu) /* voice UI? */
222 unsigned char tmp[5];
223 /* store char to pass to talk_spell */
224 unsigned char* utf8 = utf8encode(c, tmp);
225 *utf8 = 0;
227 if (c == ' ')
228 talk_id(VOICE_BLANK, false);
229 else
230 talk_spell(tmp, false);
234 #ifdef KBD_MODES
235 static void say_edit(void)
237 if (global_settings.talk_menu)
238 talk_id(VOICE_EDIT, false);
240 #endif
242 static void kbd_inschar(struct edit_state *state, unsigned short ch)
244 int i, j, len;
245 unsigned char tmp[4];
246 unsigned char* utf8;
248 len = strlen(state->text);
249 utf8 = utf8encode(ch, tmp);
250 j = (long)utf8 - (long)tmp;
252 if (len + j < state->buflen)
254 i = utf8seek(state->text, state->editpos);
255 utf8 = state->text + i;
256 memmove(utf8 + j, utf8, len - i + 1);
257 memcpy(utf8, tmp, j);
258 state->editpos++;
262 static void kbd_delchar(struct edit_state *state)
264 int i, j, len;
265 unsigned char* utf8;
267 if (state->editpos > 0)
269 state->editpos--;
270 len = strlen(state->text);
271 i = utf8seek(state->text, state->editpos);
272 utf8 = state->text + i;
273 j = utf8seek(utf8, 1);
274 memmove(utf8, utf8 + j, len - i - j + 1);
278 /* Lookup k value based on state of param (pm) */
279 static unsigned short get_kbd_ch(const struct keyboard_parameters *pm)
281 int k = (pm->page*pm->lines + pm->y)*pm->max_chars + pm->x;
282 return (k < pm->nchars)? pm->kbd_buf[k]: ' ';
285 static void kbd_calc_params(struct keyboard_parameters *pm,
286 struct screen *sc, struct edit_state *state);
287 static void kbd_draw_picker(struct keyboard_parameters *pm,
288 struct screen *sc, struct edit_state *state);
289 static void kbd_draw_edit_line(struct keyboard_parameters *pm,
290 struct screen *sc, struct edit_state *state);
292 int kbd_input(char* text, int buflen)
294 bool done = false;
295 #ifdef CPU_ARM
296 /* This seems to keep the sizes for ARM way down */
297 struct keyboard_parameters * volatile param = kbd_param;
298 #else
299 struct keyboard_parameters * const param = kbd_param;
300 #endif
301 struct edit_state state;
302 int l; /* screen loop variable */
303 unsigned short ch;
304 int ret = 0; /* assume success */
305 FOR_NB_SCREENS(l)
307 viewportmanager_theme_enable(l, false, NULL);
310 #ifdef HAVE_BUTTONBAR
311 struct gui_buttonbar buttonbar;
312 bool buttonbar_config = global_settings.buttonbar;
314 global_settings.buttonbar = true;
315 gui_buttonbar_init(&buttonbar);
316 gui_buttonbar_set_display(&buttonbar, &screens[SCREEN_MAIN]);
317 #endif
319 /* initialize state */
320 state.text = text;
321 state.buflen = buflen;
322 /* Initial edit position is after last character */
323 state.editpos = utf8length(state.text);
324 state.cur_blink = true;
325 #ifdef HAVE_MORSE_INPUT
326 state.morse_mode = global_settings.morse_input;
327 state.morse_reading = false;
328 #endif
329 state.hangul = false;
331 if (!kbd_loaded)
333 /* Copy default keyboard to buffer */
334 FOR_NB_SCREENS(l)
336 struct keyboard_parameters *pm = &param[l];
337 const unsigned char *p;
338 int i = 0;
340 #if LCD_WIDTH >= 160 && LCD_HEIGHT >= 96
341 struct screen *sc = &screens[l];
343 if (sc->getwidth() >= 160 && sc->getheight() >= 96)
345 p = "ABCDEFG abcdefg !?\" @#$%+'\n"
346 "HIJKLMN hijklmn 789 &_()-`\n"
347 "OPQRSTU opqrstu 456 §|{}/<\n"
348 "VWXYZ., vwxyz.,0123 ~=[]*>\n"
349 "ÀÁÂÃÄÅÆ ÌÍÎÏ ÈÉÊË ¢£¤¥¦§©®\n"
350 "àáâãäåæ ìíîï èéêë «»°ºª¹²³\n"
351 "ÓÒÔÕÖØ ÇÐÞÝß ÙÚÛÜ ¯±×÷¡¿µ·\n"
352 "òóôõöø çðþýÿ ùúûü ¼½¾¬¶¨:;";
354 pm->default_lines = 8;
356 else
357 #endif /* LCD_WIDTH >= 160 && LCD_HEIGHT >= 96 */
359 p = "ABCDEFG !?\" @#$%+'\n"
360 "HIJKLMN 789 &_()-`\n"
361 "OPQRSTU 456 §|{}/<\n"
362 "VWXYZ.,0123 ~=[]*>\n"
364 "abcdefg ¢£¤¥¦§©®¬\n"
365 "hijklmn «»°ºª¹²³¶\n"
366 "opqrstu ¯±×÷¡¿µ·¨\n"
367 "vwxyz., :;¼½¾ \n"
369 "ÀÁÂÃÄÅÆ ÌÍÎÏ ÈÉÊË\n"
370 "àáâãäåæ ìíîï èéêë\n"
371 "ÓÒÔÕÖØ ÇÐÞÝß ÙÚÛÜ\n"
372 "òóôõöø çðþýÿ ùúûü";
374 pm->default_lines = 4;
377 while (*p)
378 p = utf8decode(p, &pm->kbd_buf[i++]);
380 pm->nchars = i;
381 /* initialize parameters */
382 pm->x = pm->y = pm->page = 0;
384 kbd_loaded = true;
387 FOR_NB_SCREENS(l)
389 struct keyboard_parameters *pm = &param[l];
390 struct screen *sc = &screens[l];
391 kbd_calc_params(pm, sc, &state);
394 if (global_settings.talk_menu) /* voice UI? */
395 talk_spell(state.text, true); /* spell initial text */
397 while (!done)
399 /* These declarations are assigned to the screen on which the key
400 action occurred - pointers save a lot of space over array notation
401 when accessing the same array element countless times */
402 int button;
403 #if NB_SCREENS > 1
404 int button_screen;
405 #else
406 const int button_screen = 0;
407 #endif
408 struct keyboard_parameters *pm;
409 struct screen *sc;
411 state.len_utf8 = utf8length(state.text);
413 FOR_NB_SCREENS(l)
415 /* declare scoped pointers inside screen loops - hide the
416 declarations from previous block level */
417 struct keyboard_parameters *pm = &param[l];
418 struct screen *sc = &screens[l];
419 sc->clear_display();
420 kbd_draw_picker(pm, sc, &state);
421 kbd_draw_edit_line(pm, sc, &state);
424 state.cur_blink = !state.cur_blink;
426 #ifdef HAVE_BUTTONBAR
427 /* draw the button bar */
428 gui_buttonbar_set(&buttonbar, "Shift", "OK", "Del");
429 gui_buttonbar_draw(&buttonbar);
430 #endif
432 FOR_NB_SCREENS(l)
433 screens[l].update();
435 button = get_action(
436 #ifdef HAVE_MORSE_INPUT
437 state.morse_mode? CONTEXT_MORSE_INPUT:
438 #endif
439 CONTEXT_KEYBOARD, HZ/2);
440 #if NB_SCREENS > 1
441 button_screen = (get_action_statuscode(NULL) & ACTION_REMOTE) ? 1 : 0;
442 #endif
443 pm = &param[button_screen];
444 sc = &screens[button_screen];
446 #if defined(KBD_MODES) || defined(HAVE_MORSE_INPUT)
447 /* Remap some buttons to allow to move
448 * cursor in line edit mode and morse mode. */
449 #if defined(KBD_MODES) && defined(HAVE_MORSE_INPUT)
450 if (pm->line_edit || state.morse_mode)
451 #elif defined(KBD_MODES)
452 if (pm->line_edit)
453 #else /* defined(HAVE_MORSE_INPUT) */
454 if (state.morse_mode)
455 #endif
457 if (button == ACTION_KBD_LEFT)
458 button = ACTION_KBD_CURSOR_LEFT;
459 if (button == ACTION_KBD_RIGHT)
460 button = ACTION_KBD_CURSOR_RIGHT;
461 #ifdef KBD_MODES
462 /* select doubles as backspace in line_edit */
463 if (pm->line_edit && button == ACTION_KBD_SELECT)
464 button = ACTION_KBD_BACKSPACE;
465 #endif
467 #endif /* defined(KBD_MODES) || defined(HAVE_MORSE_INPUT) */
469 switch ( button )
471 case ACTION_KBD_DONE:
472 /* accepts what was entered and continues */
473 ret = 0;
474 done = true;
475 break;
477 case ACTION_KBD_ABORT:
478 ret = -1;
479 done = true;
480 break;
482 case ACTION_KBD_PAGE_FLIP:
483 #ifdef HAVE_MORSE_INPUT
484 if (state.morse_mode)
485 break;
486 #endif
487 if (++pm->page >= pm->pages)
488 pm->page = 0;
490 ch = get_kbd_ch(pm);
491 kbd_spellchar(ch);
492 break;
494 #if defined(HAVE_MORSE_INPUT) && defined(KBD_TOGGLE_INPUT)
495 case ACTION_KBD_MORSE_INPUT:
496 state.morse_mode = !state.morse_mode;
498 FOR_NB_SCREENS(l)
500 struct keyboard_parameters *pm = &param[l];
501 int y = pm->main_y;
502 pm->main_y = pm->old_main_y;
503 pm->old_main_y = y;
505 /* FIXME: We should talk something like Morse mode.. */
506 break;
507 #endif /* HAVE_MORSE_INPUT && KBD_TOGGLE_INPUT */
509 case ACTION_KBD_RIGHT:
510 if (++pm->x >= pm->max_chars)
512 #ifndef KBD_PAGE_FLIP
513 /* no dedicated flip key - flip page on wrap */
514 if (++pm->page >= pm->pages)
515 pm->page = 0;
516 #endif
517 pm->x = 0;
520 ch = get_kbd_ch(pm);
521 kbd_spellchar(ch);
522 break;
524 case ACTION_KBD_LEFT:
525 if (--pm->x < 0)
527 #ifndef KBD_PAGE_FLIP
528 /* no dedicated flip key - flip page on wrap */
529 if (--pm->page < 0)
530 pm->page = pm->pages - 1;
531 #endif
532 pm->x = pm->max_chars - 1;
535 ch = get_kbd_ch(pm);
536 kbd_spellchar(ch);
537 break;
539 case ACTION_KBD_DOWN:
540 #ifdef HAVE_MORSE_INPUT
541 if (state.morse_mode)
543 #ifdef KBD_MODES
544 pm->line_edit = !pm->line_edit;
545 if (pm->line_edit)
546 say_edit();
547 #endif
548 break;
550 #endif /* HAVE_MORSE_INPUT */
551 #ifdef KBD_MODES
552 if (pm->line_edit)
554 pm->y = 0;
555 pm->line_edit = false;
557 else if (++pm->y >= pm->lines)
559 pm->line_edit = true;
560 say_edit();
562 #else
563 if (++pm->y >= pm->lines)
564 pm->y = 0;
565 #endif
566 #ifdef KBD_MODES
567 if (!pm->line_edit)
568 #endif
570 ch = get_kbd_ch(pm);
571 kbd_spellchar(ch);
573 break;
575 case ACTION_KBD_UP:
576 #ifdef HAVE_MORSE_INPUT
577 if (state.morse_mode)
579 #ifdef KBD_MODES
580 pm->line_edit = !pm->line_edit;
581 if (pm->line_edit)
582 say_edit();
583 #endif
584 break;
586 #endif /* HAVE_MORSE_INPUT */
587 #ifdef KBD_MODES
588 if (pm->line_edit)
590 pm->y = pm->lines - 1;
591 pm->line_edit = false;
593 else if (--pm->y < 0)
595 pm->line_edit = true;
596 say_edit();
598 #else
599 if (--pm->y < 0)
600 pm->y = pm->lines - 1;
601 #endif
602 #ifdef KBD_MODES
603 if (!pm->line_edit)
604 #endif
606 ch = get_kbd_ch(pm);
607 kbd_spellchar(ch);
609 break;
611 #ifdef HAVE_MORSE_INPUT
612 case ACTION_KBD_MORSE_SELECT:
613 if (state.morse_mode && state.morse_reading)
615 state.morse_code <<= 1;
616 if ((current_tick - state.morse_tick) > HZ/5)
617 state.morse_code |= 0x01;
619 break;
620 #endif /* HAVE_MORSE_INPUT */
622 case ACTION_KBD_SELECT:
623 #ifdef HAVE_MORSE_INPUT
624 if (state.morse_mode)
626 state.morse_tick = current_tick;
628 if (!state.morse_reading)
630 state.morse_reading = true;
631 state.morse_code = 1;
634 else
635 #endif /* HAVE_MORSE_INPUT */
637 /* inserts the selected char */
638 /* find input char */
639 ch = get_kbd_ch(pm);
641 /* check for hangul input */
642 if (ch >= 0x3131 && ch <= 0x3163)
644 unsigned short tmp;
646 if (!state.hangul)
648 state.hlead = state.hvowel = state.htail = 0;
649 state.hangul = true;
652 if (!state.hvowel)
654 state.hvowel = ch;
656 else if (!state.htail)
658 state.htail = ch;
660 else
662 /* previous hangul complete */
663 /* check whether tail is actually lead of next char */
664 tmp = hangul_join(state.htail, ch, 0);
666 if (tmp != 0xfffd)
668 tmp = hangul_join(state.hlead, state.hvowel, 0);
669 kbd_delchar(&state);
670 kbd_inschar(&state, tmp);
671 /* insert dummy char */
672 kbd_inschar(&state, ' ');
673 state.hlead = state.htail;
674 state.hvowel = ch;
675 state.htail = 0;
677 else
679 state.hvowel = state.htail = 0;
680 state.hlead = ch;
684 /* combine into hangul */
685 tmp = hangul_join(state.hlead, state.hvowel, state.htail);
687 if (tmp != 0xfffd)
689 kbd_delchar(&state);
690 ch = tmp;
692 else
694 state.hvowel = state.htail = 0;
695 state.hlead = ch;
698 else
700 state.hangul = false;
703 /* insert char */
704 kbd_inschar(&state, ch);
706 if (global_settings.talk_menu) /* voice UI? */
707 talk_spell(state.text, false); /* speak revised text */
709 break;
711 case ACTION_KBD_BACKSPACE:
712 if (state.hangul)
714 if (state.htail)
715 state.htail = 0;
716 else if (state.hvowel)
717 state.hvowel = 0;
718 else
719 state.hangul = false;
722 kbd_delchar(&state);
724 if (state.hangul)
726 if (state.hvowel)
727 ch = hangul_join(state.hlead, state.hvowel, state.htail);
728 else
729 ch = state.hlead;
730 kbd_inschar(&state, ch);
733 if (global_settings.talk_menu) /* voice UI? */
734 talk_spell(state.text, false); /* speak revised text */
735 break;
737 case ACTION_KBD_CURSOR_RIGHT:
738 state.hangul = false;
740 if (state.editpos < state.len_utf8)
742 int c = utf8seek(state.text, ++state.editpos);
743 kbd_spellchar(state.text[c]);
745 #if CONFIG_CODEC == SWCODEC
746 else if (global_settings.talk_menu)
747 pcmbuf_beep(1000, 150, 1500);
748 #endif
749 break;
751 case ACTION_KBD_CURSOR_LEFT:
752 state.hangul = false;
754 if (state.editpos > 0)
756 int c = utf8seek(state.text, --state.editpos);
757 kbd_spellchar(state.text[c]);
759 #if CONFIG_CODEC == SWCODEC
760 else if (global_settings.talk_menu)
761 pcmbuf_beep(1000, 150, 1500);
762 #endif
763 break;
765 case ACTION_NONE:
766 #ifdef HAVE_MORSE_INPUT
767 if (state.morse_reading)
769 int j;
770 logf("Morse: 0x%02x", state.morse_code);
771 state.morse_reading = false;
773 for (j = 0; morse_alphabets[j] != '\0'; j++)
775 if (morse_codes[j] == state.morse_code)
776 break ;
779 if (morse_alphabets[j] == '\0')
781 logf("Morse code not found");
782 break ;
785 /* turn off hangul input */
786 state.hangul = false;
787 kbd_inschar(&state, morse_alphabets[j]);
789 if (global_settings.talk_menu) /* voice UI? */
790 talk_spell(state.text, false); /* speak revised text */
792 #endif /* HAVE_MORSE_INPUT */
793 break;
795 default:
796 if (default_event_handler(button) == SYS_USB_CONNECTED)
798 FOR_NB_SCREENS(l)
799 screens[l].setfont(FONT_SYSFIXED);
801 break;
803 } /* end switch */
805 if (button != ACTION_NONE)
807 state.cur_blink = true;
811 #ifdef HAVE_BUTTONBAR
812 global_settings.buttonbar = buttonbar_config;
813 #endif
815 if (ret < 0)
816 splash(HZ/2, ID2P(LANG_CANCEL));
818 #if defined(HAVE_MORSE_INPUT) && defined(KBD_TOGGLE_INPUT)
819 if (global_settings.morse_input != state.morse_mode)
821 global_settings.morse_input = state.morse_mode;
822 settings_save();
824 #endif /* HAVE_MORSE_INPUT && KBD_TOGGLE_INPUT */
826 FOR_NB_SCREENS(l)
828 screens[l].setfont(FONT_UI);
829 viewportmanager_theme_undo(l, false);
831 return ret;
834 static void kbd_calc_params(struct keyboard_parameters *pm,
835 struct screen *sc, struct edit_state *state)
837 struct font* font;
838 const unsigned char *p;
839 unsigned short ch;
840 int icon_w, sc_w, sc_h, w;
841 int i, total_lines;
843 pm->curfont = pm->default_lines ? FONT_SYSFIXED : FONT_UI;
844 font = font_get(pm->curfont);
845 pm->font_h = font->height;
847 /* check if FONT_UI fits the screen */
848 if (2*pm->font_h + 3 + BUTTONBAR_HEIGHT > sc->getheight())
850 pm->curfont = FONT_SYSFIXED;
851 font = font_get(FONT_SYSFIXED);
852 pm->font_h = font->height;
855 /* find max width of keyboard glyphs.
856 * since we're going to be adding spaces,
857 * max width is at least their width */
858 pm->font_w = font_get_width(font, ' ');
859 for (i = 0; i < pm->nchars; i++)
861 if (pm->kbd_buf[i] != '\n')
863 w = font_get_width(font, pm->kbd_buf[i]);
864 if (pm->font_w < w)
865 pm->font_w = w;
869 /* Find max width for text string */
870 pm->text_w = pm->font_w;
871 p = state->text;
872 while (*p)
874 p = utf8decode(p, &ch);
875 w = font_get_width(font, ch);
876 if (pm->text_w < w)
877 pm->text_w = w;
880 /* calculate how many characters to put in a row. */
881 icon_w = get_icon_width(sc->screen_type);
882 sc_w = sc->getwidth();
883 pm->max_chars = sc_w / pm->font_w;
884 pm->max_chars_text = (sc_w - icon_w * 2 - 2) / pm->text_w;
885 if (pm->max_chars_text < 3 && icon_w > pm->text_w)
886 pm->max_chars_text = sc_w / pm->text_w - 2;
889 i = 0;
890 /* Pad lines with spaces */
891 while (i < pm->nchars)
893 if (pm->kbd_buf[i] == '\n')
895 int k = pm->max_chars - i % ( pm->max_chars ) - 1;
896 int j;
898 if (k == pm->max_chars - 1)
900 pm->nchars--;
902 for (j = i; j < pm->nchars; j++)
904 pm->kbd_buf[j] = pm->kbd_buf[j + 1];
907 else
909 if (pm->nchars + k - 1 >= KBD_BUF_SIZE)
910 { /* We don't want to overflow the buffer */
911 k = KBD_BUF_SIZE - pm->nchars;
914 for (j = pm->nchars + k - 1; j > i + k; j--)
916 pm->kbd_buf[j] = pm->kbd_buf[j-k];
919 pm->nchars += k;
920 k++;
922 while (k--)
924 pm->kbd_buf[i++] = ' ';
928 else
930 i++;
934 /* calculate pm->pages and pm->lines */
935 sc_h = sc->getheight();
936 pm->lines = (sc_h - BUTTONBAR_HEIGHT) / pm->font_h - 1;
938 if (pm->default_lines && pm->lines > pm->default_lines)
939 pm->lines = pm->default_lines;
941 pm->keyboard_margin = sc_h - BUTTONBAR_HEIGHT
942 - (pm->lines+1)*pm->font_h;
944 if (pm->keyboard_margin < 3 && pm->lines > 1)
946 pm->lines--;
947 pm->keyboard_margin += pm->font_h;
950 if (pm->keyboard_margin > DEFAULT_MARGIN)
951 pm->keyboard_margin = DEFAULT_MARGIN;
953 total_lines = (pm->nchars + pm->max_chars - 1) / pm->max_chars;
954 pm->pages = (total_lines + pm->lines - 1) / pm->lines;
955 pm->lines = (total_lines + pm->pages - 1) / pm->pages;
957 pm->main_y = pm->font_h*pm->lines + pm->keyboard_margin;
958 pm->keyboard_margin -= pm->keyboard_margin/2;
960 #ifdef HAVE_MORSE_INPUT
961 pm->old_main_y = sc_h - pm->font_h - BUTTONBAR_HEIGHT;
962 if (state->morse_mode)
964 int y = pm->main_y;
965 pm->main_y = pm->old_main_y;
966 pm->old_main_y = y;
968 #endif
971 static void kbd_draw_picker(struct keyboard_parameters *pm,
972 struct screen *sc, struct edit_state *state)
974 char outline[8];
975 #ifdef HAVE_MORSE_INPUT
976 if (state->morse_mode)
978 const int w = 6, h = 8; /* sysfixed font width, height */
979 int i, j, x, y;
980 int sc_w = sc->getwidth(), sc_h = pm->main_y - pm->keyboard_margin - 1;
982 /* Draw morse code screen with sysfont */
983 sc->setfont(FONT_SYSFIXED);
984 x = 0;
985 y = 0;
986 outline[1] = '\0';
988 /* Draw morse code table with code descriptions. */
989 for (i = 0; morse_alphabets[i] != '\0'; i++)
991 int morse_code;
993 outline[0] = morse_alphabets[i];
994 sc->putsxy(x, y, outline);
996 morse_code = morse_codes[i];
997 for (j = 0; morse_code > 0x01; morse_code >>= 1)
998 j++;
1000 x += w + 3 + j*4;
1001 morse_code = morse_codes[i];
1002 for (; morse_code > 0x01; morse_code >>= 1)
1004 x -= 4;
1005 if (morse_code & 0x01)
1006 sc->fillrect(x, y + 2, 3, 4);
1007 else
1008 sc->fillrect(x, y + 3, 1, 2);
1011 x += w*5 - 3;
1012 if (x + w*6 >= sc_w)
1014 x = 0;
1015 y += h;
1016 if (y + h >= sc_h)
1017 break;
1021 else
1022 #else
1023 (void) state;
1024 #endif /* HAVE_MORSE_INPUT */
1026 /* draw page */
1027 int i, j, k;
1029 sc->setfont(pm->curfont);
1031 k = pm->page*pm->max_chars*pm->lines;
1033 for (i = j = 0; k < pm->nchars; k++)
1035 int w;
1036 unsigned char *utf8;
1037 utf8 = utf8encode(pm->kbd_buf[k], outline);
1038 *utf8 = 0;
1040 sc->getstringsize(outline, &w, NULL);
1041 sc->putsxy(i*pm->font_w + (pm->font_w-w) / 2,
1042 j*pm->font_h, outline);
1044 if (++i >= pm->max_chars)
1046 i = 0;
1047 if (++j >= pm->lines)
1048 break;
1052 #ifdef KBD_MODES
1053 if (!pm->line_edit)
1054 #endif
1056 /* highlight the key that has focus */
1057 sc->set_drawmode(DRMODE_COMPLEMENT);
1058 sc->fillrect(pm->font_w*pm->x, pm->font_h*pm->y,
1059 pm->font_w, pm->font_h);
1060 sc->set_drawmode(DRMODE_SOLID);
1065 static void kbd_draw_edit_line(struct keyboard_parameters *pm,
1066 struct screen *sc, struct edit_state *state)
1068 char outline[8];
1069 unsigned char *utf8;
1070 int i = 0, j = 0, icon_w, w;
1071 int sc_w = sc->getwidth();
1072 int y = pm->main_y - pm->keyboard_margin;
1073 int text_margin = (sc_w - pm->text_w * pm->max_chars_text) / 2;
1075 /* Clear text area one pixel above separator line so any overdraw
1076 doesn't collide */
1077 screen_clear_area(sc, 0, y - 1, sc_w, pm->font_h + 6);
1079 sc->hline(0, sc_w - 1, y);
1081 /* write out the text */
1082 sc->setfont(pm->curfont);
1084 pm->leftpos = MAX(0, MIN(state->len_utf8, state->editpos + 2)
1085 - pm->max_chars_text);
1086 pm->curpos = state->editpos - pm->leftpos;
1087 utf8 = state->text + utf8seek(state->text, pm->leftpos);
1089 while (*utf8 && i < pm->max_chars_text)
1091 j = utf8seek(utf8, 1);
1092 strlcpy(outline, utf8, j+1);
1093 sc->getstringsize(outline, &w, NULL);
1094 sc->putsxy(text_margin + i*pm->text_w + (pm->text_w-w)/2,
1095 pm->main_y, outline);
1096 utf8 += j;
1097 i++;
1100 icon_w = get_icon_width(sc->screen_type);
1101 if (pm->leftpos > 0)
1103 /* Draw nicer bitmap arrow if room, else settle for "<". */
1104 if (text_margin >= icon_w)
1106 screen_put_icon_with_offset(sc, 0, 0,
1107 (text_margin - icon_w) / 2,
1108 pm->main_y, Icon_Reverse_Cursor);
1110 else
1112 sc->getstringsize("<", &w, NULL);
1113 sc->putsxy(text_margin - w, pm->main_y, "<");
1117 if (state->len_utf8 - pm->leftpos > pm->max_chars_text)
1119 /* Draw nicer bitmap arrow if room, else settle for ">". */
1120 if (text_margin >= icon_w)
1122 screen_put_icon_with_offset(sc, 0, 0,
1123 sc_w - (text_margin + icon_w) / 2,
1124 pm->main_y, Icon_Cursor);
1126 else
1128 sc->putsxy(sc_w - text_margin, pm->main_y, ">");
1132 /* cursor */
1133 i = text_margin + pm->curpos * pm->text_w;
1135 if (state->cur_blink)
1136 sc->vline(i, pm->main_y, pm->main_y + pm->font_h - 1);
1138 if (state->hangul) /* draw underbar */
1139 sc->hline(i - pm->text_w, i, pm->main_y + pm->font_h - 1);
1141 #ifdef KBD_MODES
1142 if (pm->line_edit)
1144 sc->set_drawmode(DRMODE_COMPLEMENT);
1145 sc->fillrect(0, y + 2, sc_w, pm->font_h + 2);
1146 sc->set_drawmode(DRMODE_SOLID);
1148 #endif