Packard Bell Vibe 500: correct main keymaps, enable full keyboard editing, enable...
[kugel-rb.git] / apps / recorder / keyboard.c
blobfdf1f3a433cd477e180f53a9ae8d61680896c8a5
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 || (CONFIG_KEYPAD == PBELL_VIBE500_PAD) \
59 /* no key combos to move the cursor if not in line edit mode */
60 #define KBD_MODES /* uses 2 modes, picker and line edit */
62 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) \
63 || (CONFIG_KEYPAD == IRIVER_H300_PAD) \
64 || (CONFIG_KEYPAD == GIGABEAT_PAD) \
65 || (CONFIG_KEYPAD == GIGABEAT_S_PAD) \
66 || (CONFIG_KEYPAD == SANSA_E200_PAD) \
67 || (CONFIG_KEYPAD == SANSA_FUZE_PAD) \
68 || (CONFIG_KEYPAD == SANSA_C200_PAD) \
69 || (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
70 /* certain key combos move the cursor even if not in line edit mode */
71 #define KBD_CURSOR_KEYS
72 #define KBD_MODES /* uses 2 modes, picker and line edit */
74 #else
75 #define KBD_CURSOR_KEYS /* certain keys move the cursor, no line edit mode */
76 #endif
78 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) \
79 || (CONFIG_KEYPAD == IRIVER_H300_PAD) \
80 || (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
81 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
82 || (CONFIG_KEYPAD == IPOD_4G_PAD) \
83 || (CONFIG_KEYPAD == IRIVER_H10_PAD) \
84 || (CONFIG_KEYPAD == GIGABEAT_PAD) \
85 || (CONFIG_KEYPAD == GIGABEAT_S_PAD) \
86 || (CONFIG_KEYPAD == MROBE100_PAD) \
87 || (CONFIG_KEYPAD == SANSA_E200_PAD) \
88 || (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD) \
89 || (CONFIG_KEYPAD == PHILIPS_SA9200_PAD) \
90 || (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
91 /* certain key combos toggle input mode between keyboard input and Morse input */
92 #define KBD_TOGGLE_INPUT
93 #endif
95 struct keyboard_parameters
97 unsigned short kbd_buf[KBD_BUF_SIZE];
98 int default_lines;
99 int nchars;
100 int font_w;
101 int font_h;
102 int text_w;
103 int curfont;
104 int main_y;
105 #ifdef HAVE_MORSE_INPUT
106 int old_main_y;
107 #endif
108 int max_chars;
109 int max_chars_text;
110 int lines;
111 int pages;
112 int keyboard_margin;
113 int curpos;
114 int leftpos;
115 int page;
116 int x;
117 int y;
118 #ifdef KBD_MODES
119 bool line_edit;
120 #endif
123 struct edit_state
125 char* text;
126 int buflen;
127 int len_utf8;
128 int editpos; /* Edit position on all screens */
129 bool cur_blink; /* Cursor on/off flag */
130 bool hangul;
131 unsigned short hlead, hvowel, htail;
132 #ifdef HAVE_MORSE_INPUT
133 bool morse_mode;
134 bool morse_reading;
135 unsigned char morse_code;
136 int morse_tick;
137 #endif
140 static struct keyboard_parameters kbd_param[NB_SCREENS];
141 static bool kbd_loaded = false;
143 #ifdef HAVE_MORSE_INPUT
144 /* FIXME: We should put this to a configuration file. */
145 static const char *morse_alphabets =
146 "abcdefghijklmnopqrstuvwxyz1234567890,.?-@ ";
147 static const unsigned char morse_codes[] = {
148 0x05,0x18,0x1a,0x0c,0x02,0x12,0x0e,0x10,0x04,0x17,0x0d,0x14,0x07,
149 0x06,0x0f,0x16,0x1d,0x0a,0x08,0x03,0x09,0x11,0x0b,0x19,0x1b,0x1c,
150 0x2f,0x27,0x23,0x21,0x20,0x30,0x38,0x3c,0x3e,0x3f,
151 0x73,0x55,0x4c,0x61,0x5a,0x80 };
152 #endif
154 /* Loads a custom keyboard into memory
155 call with NULL to reset keyboard */
156 int load_kbd(unsigned char* filename)
158 int fd, l;
159 int i = 0;
160 unsigned char buf[4];
162 if (filename == NULL)
164 kbd_loaded = false;
165 return 0;
168 fd = open_utf8(filename, O_RDONLY|O_BINARY);
169 if (fd < 0)
170 return 1;
172 while (read(fd, buf, 1) == 1 && i < KBD_BUF_SIZE)
174 /* check how many bytes to read for this character */
175 static const unsigned char sizes[4] = { 0x80, 0xe0, 0xf0, 0xf5 };
176 size_t count;
177 unsigned short ch;
179 for (count = 0; count < ARRAYLEN(sizes); count++)
181 if (buf[0] < sizes[count])
182 break;
185 if (count >= ARRAYLEN(sizes))
186 continue; /* Invalid size. */
188 if (read(fd, &buf[1], count) != (ssize_t)count)
190 close(fd);
191 kbd_loaded = false;
192 return 1;
195 utf8decode(buf, &ch);
196 if (ch != 0xFEFF && ch != '\r') /* skip BOM & carriage returns */
198 FOR_NB_SCREENS(l)
199 kbd_param[l].kbd_buf[i] = ch;
200 i++;
204 close(fd);
205 kbd_loaded = true;
207 FOR_NB_SCREENS(l)
209 struct keyboard_parameters *pm = &kbd_param[l];
210 pm->nchars = i;
211 /* initialize parameters */
212 pm->x = pm->y = pm->page = 0;
213 pm->default_lines = 0;
216 return 0;
219 /* helper function to spell a char if voice UI is enabled */
220 static void kbd_spellchar(unsigned short c)
222 if (global_settings.talk_menu) /* voice UI? */
224 unsigned char tmp[5];
225 /* store char to pass to talk_spell */
226 unsigned char* utf8 = utf8encode(c, tmp);
227 *utf8 = 0;
229 if (c == ' ')
230 talk_id(VOICE_BLANK, false);
231 else
232 talk_spell(tmp, false);
236 #ifdef KBD_MODES
237 static void say_edit(void)
239 if (global_settings.talk_menu)
240 talk_id(VOICE_EDIT, false);
242 #endif
244 static void kbd_inschar(struct edit_state *state, unsigned short ch)
246 int i, j, len;
247 unsigned char tmp[4];
248 unsigned char* utf8;
250 len = strlen(state->text);
251 utf8 = utf8encode(ch, tmp);
252 j = (long)utf8 - (long)tmp;
254 if (len + j < state->buflen)
256 i = utf8seek(state->text, state->editpos);
257 utf8 = state->text + i;
258 memmove(utf8 + j, utf8, len - i + 1);
259 memcpy(utf8, tmp, j);
260 state->editpos++;
264 static void kbd_delchar(struct edit_state *state)
266 int i, j, len;
267 unsigned char* utf8;
269 if (state->editpos > 0)
271 state->editpos--;
272 len = strlen(state->text);
273 i = utf8seek(state->text, state->editpos);
274 utf8 = state->text + i;
275 j = utf8seek(utf8, 1);
276 memmove(utf8, utf8 + j, len - i - j + 1);
280 /* Lookup k value based on state of param (pm) */
281 static unsigned short get_kbd_ch(const struct keyboard_parameters *pm)
283 int k = (pm->page*pm->lines + pm->y)*pm->max_chars + pm->x;
284 return (k < pm->nchars)? pm->kbd_buf[k]: ' ';
287 static void kbd_calc_params(struct keyboard_parameters *pm,
288 struct screen *sc, struct edit_state *state);
289 static void kbd_draw_picker(struct keyboard_parameters *pm,
290 struct screen *sc, struct edit_state *state);
291 static void kbd_draw_edit_line(struct keyboard_parameters *pm,
292 struct screen *sc, struct edit_state *state);
294 int kbd_input(char* text, int buflen)
296 bool done = false;
297 #ifdef CPU_ARM
298 /* This seems to keep the sizes for ARM way down */
299 struct keyboard_parameters * volatile param = kbd_param;
300 #else
301 struct keyboard_parameters * const param = kbd_param;
302 #endif
303 struct edit_state state;
304 int l; /* screen loop variable */
305 unsigned short ch;
306 int ret = 0; /* assume success */
307 FOR_NB_SCREENS(l)
309 viewportmanager_theme_enable(l, false, NULL);
312 #ifdef HAVE_BUTTONBAR
313 struct gui_buttonbar buttonbar;
314 bool buttonbar_config = global_settings.buttonbar;
316 global_settings.buttonbar = true;
317 gui_buttonbar_init(&buttonbar);
318 gui_buttonbar_set_display(&buttonbar, &screens[SCREEN_MAIN]);
319 #endif
321 /* initialize state */
322 state.text = text;
323 state.buflen = buflen;
324 /* Initial edit position is after last character */
325 state.editpos = utf8length(state.text);
326 state.cur_blink = true;
327 #ifdef HAVE_MORSE_INPUT
328 state.morse_mode = global_settings.morse_input;
329 state.morse_reading = false;
330 #endif
331 state.hangul = false;
333 if (!kbd_loaded)
335 /* Copy default keyboard to buffer */
336 FOR_NB_SCREENS(l)
338 struct keyboard_parameters *pm = &param[l];
339 const unsigned char *p;
340 int i = 0;
342 #if LCD_WIDTH >= 160 && LCD_HEIGHT >= 96
343 struct screen *sc = &screens[l];
345 if (sc->getwidth() >= 160 && sc->getheight() >= 96)
347 p = "ABCDEFG abcdefg !?\" @#$%+'\n"
348 "HIJKLMN hijklmn 789 &_()-`\n"
349 "OPQRSTU opqrstu 456 §|{}/<\n"
350 "VWXYZ., vwxyz.,0123 ~=[]*>\n"
351 "ÀÁÂÃÄÅÆ ÌÍÎÏ ÈÉÊË ¢£¤¥¦§©®\n"
352 "àáâãäåæ ìíîï èéêë «»°ºª¹²³\n"
353 "ÓÒÔÕÖØ ÇÐÞÝß ÙÚÛÜ ¯±×÷¡¿µ·\n"
354 "òóôõöø çðþýÿ ùúûü ¼½¾¬¶¨:;";
356 pm->default_lines = 8;
358 else
359 #endif /* LCD_WIDTH >= 160 && LCD_HEIGHT >= 96 */
361 p = "ABCDEFG !?\" @#$%+'\n"
362 "HIJKLMN 789 &_()-`\n"
363 "OPQRSTU 456 §|{}/<\n"
364 "VWXYZ.,0123 ~=[]*>\n"
366 "abcdefg ¢£¤¥¦§©®¬\n"
367 "hijklmn «»°ºª¹²³¶\n"
368 "opqrstu ¯±×÷¡¿µ·¨\n"
369 "vwxyz., :;¼½¾ \n"
371 "ÀÁÂÃÄÅÆ ÌÍÎÏ ÈÉÊË\n"
372 "àáâãäåæ ìíîï èéêë\n"
373 "ÓÒÔÕÖØ ÇÐÞÝß ÙÚÛÜ\n"
374 "òóôõöø çðþýÿ ùúûü";
376 pm->default_lines = 4;
379 while (*p)
380 p = utf8decode(p, &pm->kbd_buf[i++]);
382 pm->nchars = i;
383 /* initialize parameters */
384 pm->x = pm->y = pm->page = 0;
386 kbd_loaded = true;
389 FOR_NB_SCREENS(l)
391 struct keyboard_parameters *pm = &param[l];
392 struct screen *sc = &screens[l];
393 kbd_calc_params(pm, sc, &state);
396 if (global_settings.talk_menu) /* voice UI? */
397 talk_spell(state.text, true); /* spell initial text */
399 while (!done)
401 /* These declarations are assigned to the screen on which the key
402 action occurred - pointers save a lot of space over array notation
403 when accessing the same array element countless times */
404 int button;
405 #if NB_SCREENS > 1
406 int button_screen;
407 #else
408 const int button_screen = 0;
409 #endif
410 struct keyboard_parameters *pm;
411 struct screen *sc;
413 state.len_utf8 = utf8length(state.text);
415 FOR_NB_SCREENS(l)
417 /* declare scoped pointers inside screen loops - hide the
418 declarations from previous block level */
419 struct keyboard_parameters *pm = &param[l];
420 struct screen *sc = &screens[l];
421 sc->clear_display();
422 kbd_draw_picker(pm, sc, &state);
423 kbd_draw_edit_line(pm, sc, &state);
426 state.cur_blink = !state.cur_blink;
428 #ifdef HAVE_BUTTONBAR
429 /* draw the button bar */
430 gui_buttonbar_set(&buttonbar, "Shift", "OK", "Del");
431 gui_buttonbar_draw(&buttonbar);
432 #endif
434 FOR_NB_SCREENS(l)
435 screens[l].update();
437 button = get_action(
438 #ifdef HAVE_MORSE_INPUT
439 state.morse_mode? CONTEXT_MORSE_INPUT:
440 #endif
441 CONTEXT_KEYBOARD, HZ/2);
442 #if NB_SCREENS > 1
443 button_screen = (get_action_statuscode(NULL) & ACTION_REMOTE) ? 1 : 0;
444 #endif
445 pm = &param[button_screen];
446 sc = &screens[button_screen];
448 #if defined(KBD_MODES) || defined(HAVE_MORSE_INPUT)
449 /* Remap some buttons to allow to move
450 * cursor in line edit mode and morse mode. */
451 #if defined(KBD_MODES) && defined(HAVE_MORSE_INPUT)
452 if (pm->line_edit || state.morse_mode)
453 #elif defined(KBD_MODES)
454 if (pm->line_edit)
455 #else /* defined(HAVE_MORSE_INPUT) */
456 if (state.morse_mode)
457 #endif
459 if (button == ACTION_KBD_LEFT)
460 button = ACTION_KBD_CURSOR_LEFT;
461 if (button == ACTION_KBD_RIGHT)
462 button = ACTION_KBD_CURSOR_RIGHT;
463 #ifdef KBD_MODES
464 /* select doubles as backspace in line_edit */
465 if (pm->line_edit && button == ACTION_KBD_SELECT)
466 button = ACTION_KBD_BACKSPACE;
467 #endif
469 #endif /* defined(KBD_MODES) || defined(HAVE_MORSE_INPUT) */
471 switch ( button )
473 case ACTION_KBD_DONE:
474 /* accepts what was entered and continues */
475 ret = 0;
476 done = true;
477 break;
479 case ACTION_KBD_ABORT:
480 ret = -1;
481 done = true;
482 break;
484 case ACTION_KBD_PAGE_FLIP:
485 #ifdef HAVE_MORSE_INPUT
486 if (state.morse_mode)
487 break;
488 #endif
489 if (++pm->page >= pm->pages)
490 pm->page = 0;
492 ch = get_kbd_ch(pm);
493 kbd_spellchar(ch);
494 break;
496 #if defined(HAVE_MORSE_INPUT) && defined(KBD_TOGGLE_INPUT)
497 case ACTION_KBD_MORSE_INPUT:
498 state.morse_mode = !state.morse_mode;
500 FOR_NB_SCREENS(l)
502 struct keyboard_parameters *pm = &param[l];
503 int y = pm->main_y;
504 pm->main_y = pm->old_main_y;
505 pm->old_main_y = y;
507 /* FIXME: We should talk something like Morse mode.. */
508 break;
509 #endif /* HAVE_MORSE_INPUT && KBD_TOGGLE_INPUT */
511 case ACTION_KBD_RIGHT:
512 if (++pm->x >= pm->max_chars)
514 #ifndef KBD_PAGE_FLIP
515 /* no dedicated flip key - flip page on wrap */
516 if (++pm->page >= pm->pages)
517 pm->page = 0;
518 #endif
519 pm->x = 0;
522 ch = get_kbd_ch(pm);
523 kbd_spellchar(ch);
524 break;
526 case ACTION_KBD_LEFT:
527 if (--pm->x < 0)
529 #ifndef KBD_PAGE_FLIP
530 /* no dedicated flip key - flip page on wrap */
531 if (--pm->page < 0)
532 pm->page = pm->pages - 1;
533 #endif
534 pm->x = pm->max_chars - 1;
537 ch = get_kbd_ch(pm);
538 kbd_spellchar(ch);
539 break;
541 case ACTION_KBD_DOWN:
542 #ifdef HAVE_MORSE_INPUT
543 if (state.morse_mode)
545 #ifdef KBD_MODES
546 pm->line_edit = !pm->line_edit;
547 if (pm->line_edit)
548 say_edit();
549 #endif
550 break;
552 #endif /* HAVE_MORSE_INPUT */
553 #ifdef KBD_MODES
554 if (pm->line_edit)
556 pm->y = 0;
557 pm->line_edit = false;
559 else if (++pm->y >= pm->lines)
561 pm->line_edit = true;
562 say_edit();
564 #else
565 if (++pm->y >= pm->lines)
566 pm->y = 0;
567 #endif
568 #ifdef KBD_MODES
569 if (!pm->line_edit)
570 #endif
572 ch = get_kbd_ch(pm);
573 kbd_spellchar(ch);
575 break;
577 case ACTION_KBD_UP:
578 #ifdef HAVE_MORSE_INPUT
579 if (state.morse_mode)
581 #ifdef KBD_MODES
582 pm->line_edit = !pm->line_edit;
583 if (pm->line_edit)
584 say_edit();
585 #endif
586 break;
588 #endif /* HAVE_MORSE_INPUT */
589 #ifdef KBD_MODES
590 if (pm->line_edit)
592 pm->y = pm->lines - 1;
593 pm->line_edit = false;
595 else if (--pm->y < 0)
597 pm->line_edit = true;
598 say_edit();
600 #else
601 if (--pm->y < 0)
602 pm->y = pm->lines - 1;
603 #endif
604 #ifdef KBD_MODES
605 if (!pm->line_edit)
606 #endif
608 ch = get_kbd_ch(pm);
609 kbd_spellchar(ch);
611 break;
613 #ifdef HAVE_MORSE_INPUT
614 case ACTION_KBD_MORSE_SELECT:
615 if (state.morse_mode && state.morse_reading)
617 state.morse_code <<= 1;
618 if ((current_tick - state.morse_tick) > HZ/5)
619 state.morse_code |= 0x01;
621 break;
622 #endif /* HAVE_MORSE_INPUT */
624 case ACTION_KBD_SELECT:
625 #ifdef HAVE_MORSE_INPUT
626 if (state.morse_mode)
628 state.morse_tick = current_tick;
630 if (!state.morse_reading)
632 state.morse_reading = true;
633 state.morse_code = 1;
636 else
637 #endif /* HAVE_MORSE_INPUT */
639 /* inserts the selected char */
640 /* find input char */
641 ch = get_kbd_ch(pm);
643 /* check for hangul input */
644 if (ch >= 0x3131 && ch <= 0x3163)
646 unsigned short tmp;
648 if (!state.hangul)
650 state.hlead = state.hvowel = state.htail = 0;
651 state.hangul = true;
654 if (!state.hvowel)
656 state.hvowel = ch;
658 else if (!state.htail)
660 state.htail = ch;
662 else
664 /* previous hangul complete */
665 /* check whether tail is actually lead of next char */
666 tmp = hangul_join(state.htail, ch, 0);
668 if (tmp != 0xfffd)
670 tmp = hangul_join(state.hlead, state.hvowel, 0);
671 kbd_delchar(&state);
672 kbd_inschar(&state, tmp);
673 /* insert dummy char */
674 kbd_inschar(&state, ' ');
675 state.hlead = state.htail;
676 state.hvowel = ch;
677 state.htail = 0;
679 else
681 state.hvowel = state.htail = 0;
682 state.hlead = ch;
686 /* combine into hangul */
687 tmp = hangul_join(state.hlead, state.hvowel, state.htail);
689 if (tmp != 0xfffd)
691 kbd_delchar(&state);
692 ch = tmp;
694 else
696 state.hvowel = state.htail = 0;
697 state.hlead = ch;
700 else
702 state.hangul = false;
705 /* insert char */
706 kbd_inschar(&state, ch);
708 if (global_settings.talk_menu) /* voice UI? */
709 talk_spell(state.text, false); /* speak revised text */
711 break;
713 case ACTION_KBD_BACKSPACE:
714 if (state.hangul)
716 if (state.htail)
717 state.htail = 0;
718 else if (state.hvowel)
719 state.hvowel = 0;
720 else
721 state.hangul = false;
724 kbd_delchar(&state);
726 if (state.hangul)
728 if (state.hvowel)
729 ch = hangul_join(state.hlead, state.hvowel, state.htail);
730 else
731 ch = state.hlead;
732 kbd_inschar(&state, ch);
735 if (global_settings.talk_menu) /* voice UI? */
736 talk_spell(state.text, false); /* speak revised text */
737 break;
739 case ACTION_KBD_CURSOR_RIGHT:
740 state.hangul = false;
742 if (state.editpos < state.len_utf8)
744 int c = utf8seek(state.text, ++state.editpos);
745 kbd_spellchar(state.text[c]);
747 #if CONFIG_CODEC == SWCODEC
748 else if (global_settings.talk_menu)
749 pcmbuf_beep(1000, 150, 1500);
750 #endif
751 break;
753 case ACTION_KBD_CURSOR_LEFT:
754 state.hangul = false;
756 if (state.editpos > 0)
758 int c = utf8seek(state.text, --state.editpos);
759 kbd_spellchar(state.text[c]);
761 #if CONFIG_CODEC == SWCODEC
762 else if (global_settings.talk_menu)
763 pcmbuf_beep(1000, 150, 1500);
764 #endif
765 break;
767 case ACTION_NONE:
768 #ifdef HAVE_MORSE_INPUT
769 if (state.morse_reading)
771 int j;
772 logf("Morse: 0x%02x", state.morse_code);
773 state.morse_reading = false;
775 for (j = 0; morse_alphabets[j] != '\0'; j++)
777 if (morse_codes[j] == state.morse_code)
778 break ;
781 if (morse_alphabets[j] == '\0')
783 logf("Morse code not found");
784 break ;
787 /* turn off hangul input */
788 state.hangul = false;
789 kbd_inschar(&state, morse_alphabets[j]);
791 if (global_settings.talk_menu) /* voice UI? */
792 talk_spell(state.text, false); /* speak revised text */
794 #endif /* HAVE_MORSE_INPUT */
795 break;
797 default:
798 if (default_event_handler(button) == SYS_USB_CONNECTED)
800 FOR_NB_SCREENS(l)
801 screens[l].setfont(FONT_SYSFIXED);
803 break;
805 } /* end switch */
807 if (button != ACTION_NONE)
809 state.cur_blink = true;
813 #ifdef HAVE_BUTTONBAR
814 global_settings.buttonbar = buttonbar_config;
815 #endif
817 if (ret < 0)
818 splash(HZ/2, ID2P(LANG_CANCEL));
820 #if defined(HAVE_MORSE_INPUT) && defined(KBD_TOGGLE_INPUT)
821 if (global_settings.morse_input != state.morse_mode)
823 global_settings.morse_input = state.morse_mode;
824 settings_save();
826 #endif /* HAVE_MORSE_INPUT && KBD_TOGGLE_INPUT */
828 FOR_NB_SCREENS(l)
830 screens[l].setfont(FONT_UI);
831 viewportmanager_theme_undo(l, false);
833 return ret;
836 static void kbd_calc_params(struct keyboard_parameters *pm,
837 struct screen *sc, struct edit_state *state)
839 struct font* font;
840 const unsigned char *p;
841 unsigned short ch;
842 int icon_w, sc_w, sc_h, w;
843 int i, total_lines;
845 pm->curfont = pm->default_lines ? FONT_SYSFIXED : FONT_UI;
846 font = font_get(pm->curfont);
847 pm->font_h = font->height;
849 /* check if FONT_UI fits the screen */
850 if (2*pm->font_h + 3 + BUTTONBAR_HEIGHT > sc->getheight())
852 pm->curfont = FONT_SYSFIXED;
853 font = font_get(FONT_SYSFIXED);
854 pm->font_h = font->height;
857 /* find max width of keyboard glyphs.
858 * since we're going to be adding spaces,
859 * max width is at least their width */
860 pm->font_w = font_get_width(font, ' ');
861 for (i = 0; i < pm->nchars; i++)
863 if (pm->kbd_buf[i] != '\n')
865 w = font_get_width(font, pm->kbd_buf[i]);
866 if (pm->font_w < w)
867 pm->font_w = w;
871 /* Find max width for text string */
872 pm->text_w = pm->font_w;
873 p = state->text;
874 while (*p)
876 p = utf8decode(p, &ch);
877 w = font_get_width(font, ch);
878 if (pm->text_w < w)
879 pm->text_w = w;
882 /* calculate how many characters to put in a row. */
883 icon_w = get_icon_width(sc->screen_type);
884 sc_w = sc->getwidth();
885 pm->max_chars = sc_w / pm->font_w;
886 pm->max_chars_text = (sc_w - icon_w * 2 - 2) / pm->text_w;
887 if (pm->max_chars_text < 3 && icon_w > pm->text_w)
888 pm->max_chars_text = sc_w / pm->text_w - 2;
891 i = 0;
892 /* Pad lines with spaces */
893 while (i < pm->nchars)
895 if (pm->kbd_buf[i] == '\n')
897 int k = pm->max_chars - i % ( pm->max_chars ) - 1;
898 int j;
900 if (k == pm->max_chars - 1)
902 pm->nchars--;
904 for (j = i; j < pm->nchars; j++)
906 pm->kbd_buf[j] = pm->kbd_buf[j + 1];
909 else
911 if (pm->nchars + k - 1 >= KBD_BUF_SIZE)
912 { /* We don't want to overflow the buffer */
913 k = KBD_BUF_SIZE - pm->nchars;
916 for (j = pm->nchars + k - 1; j > i + k; j--)
918 pm->kbd_buf[j] = pm->kbd_buf[j-k];
921 pm->nchars += k;
922 k++;
924 while (k--)
926 pm->kbd_buf[i++] = ' ';
930 else
932 i++;
936 /* calculate pm->pages and pm->lines */
937 sc_h = sc->getheight();
938 pm->lines = (sc_h - BUTTONBAR_HEIGHT) / pm->font_h - 1;
940 if (pm->default_lines && pm->lines > pm->default_lines)
941 pm->lines = pm->default_lines;
943 pm->keyboard_margin = sc_h - BUTTONBAR_HEIGHT
944 - (pm->lines+1)*pm->font_h;
946 if (pm->keyboard_margin < 3 && pm->lines > 1)
948 pm->lines--;
949 pm->keyboard_margin += pm->font_h;
952 if (pm->keyboard_margin > DEFAULT_MARGIN)
953 pm->keyboard_margin = DEFAULT_MARGIN;
955 total_lines = (pm->nchars + pm->max_chars - 1) / pm->max_chars;
956 pm->pages = (total_lines + pm->lines - 1) / pm->lines;
957 pm->lines = (total_lines + pm->pages - 1) / pm->pages;
959 pm->main_y = pm->font_h*pm->lines + pm->keyboard_margin;
960 pm->keyboard_margin -= pm->keyboard_margin/2;
962 #ifdef HAVE_MORSE_INPUT
963 pm->old_main_y = sc_h - pm->font_h - BUTTONBAR_HEIGHT;
964 if (state->morse_mode)
966 int y = pm->main_y;
967 pm->main_y = pm->old_main_y;
968 pm->old_main_y = y;
970 #endif
973 static void kbd_draw_picker(struct keyboard_parameters *pm,
974 struct screen *sc, struct edit_state *state)
976 char outline[8];
977 #ifdef HAVE_MORSE_INPUT
978 if (state->morse_mode)
980 const int w = 6, h = 8; /* sysfixed font width, height */
981 int i, j, x, y;
982 int sc_w = sc->getwidth(), sc_h = pm->main_y - pm->keyboard_margin - 1;
984 /* Draw morse code screen with sysfont */
985 sc->setfont(FONT_SYSFIXED);
986 x = 0;
987 y = 0;
988 outline[1] = '\0';
990 /* Draw morse code table with code descriptions. */
991 for (i = 0; morse_alphabets[i] != '\0'; i++)
993 int morse_code;
995 outline[0] = morse_alphabets[i];
996 sc->putsxy(x, y, outline);
998 morse_code = morse_codes[i];
999 for (j = 0; morse_code > 0x01; morse_code >>= 1)
1000 j++;
1002 x += w + 3 + j*4;
1003 morse_code = morse_codes[i];
1004 for (; morse_code > 0x01; morse_code >>= 1)
1006 x -= 4;
1007 if (morse_code & 0x01)
1008 sc->fillrect(x, y + 2, 3, 4);
1009 else
1010 sc->fillrect(x, y + 3, 1, 2);
1013 x += w*5 - 3;
1014 if (x + w*6 >= sc_w)
1016 x = 0;
1017 y += h;
1018 if (y + h >= sc_h)
1019 break;
1023 else
1024 #else
1025 (void) state;
1026 #endif /* HAVE_MORSE_INPUT */
1028 /* draw page */
1029 int i, j, k;
1031 sc->setfont(pm->curfont);
1033 k = pm->page*pm->max_chars*pm->lines;
1035 for (i = j = 0; k < pm->nchars; k++)
1037 int w;
1038 unsigned char *utf8;
1039 utf8 = utf8encode(pm->kbd_buf[k], outline);
1040 *utf8 = 0;
1042 sc->getstringsize(outline, &w, NULL);
1043 sc->putsxy(i*pm->font_w + (pm->font_w-w) / 2,
1044 j*pm->font_h, outline);
1046 if (++i >= pm->max_chars)
1048 i = 0;
1049 if (++j >= pm->lines)
1050 break;
1054 #ifdef KBD_MODES
1055 if (!pm->line_edit)
1056 #endif
1058 /* highlight the key that has focus */
1059 sc->set_drawmode(DRMODE_COMPLEMENT);
1060 sc->fillrect(pm->font_w*pm->x, pm->font_h*pm->y,
1061 pm->font_w, pm->font_h);
1062 sc->set_drawmode(DRMODE_SOLID);
1067 static void kbd_draw_edit_line(struct keyboard_parameters *pm,
1068 struct screen *sc, struct edit_state *state)
1070 char outline[8];
1071 unsigned char *utf8;
1072 int i = 0, j = 0, icon_w, w;
1073 int sc_w = sc->getwidth();
1074 int y = pm->main_y - pm->keyboard_margin;
1075 int text_margin = (sc_w - pm->text_w * pm->max_chars_text) / 2;
1077 /* Clear text area one pixel above separator line so any overdraw
1078 doesn't collide */
1079 screen_clear_area(sc, 0, y - 1, sc_w, pm->font_h + 6);
1081 sc->hline(0, sc_w - 1, y);
1083 /* write out the text */
1084 sc->setfont(pm->curfont);
1086 pm->leftpos = MAX(0, MIN(state->len_utf8, state->editpos + 2)
1087 - pm->max_chars_text);
1088 pm->curpos = state->editpos - pm->leftpos;
1089 utf8 = state->text + utf8seek(state->text, pm->leftpos);
1091 while (*utf8 && i < pm->max_chars_text)
1093 j = utf8seek(utf8, 1);
1094 strlcpy(outline, utf8, j+1);
1095 sc->getstringsize(outline, &w, NULL);
1096 sc->putsxy(text_margin + i*pm->text_w + (pm->text_w-w)/2,
1097 pm->main_y, outline);
1098 utf8 += j;
1099 i++;
1102 icon_w = get_icon_width(sc->screen_type);
1103 if (pm->leftpos > 0)
1105 /* Draw nicer bitmap arrow if room, else settle for "<". */
1106 if (text_margin >= icon_w)
1108 screen_put_icon_with_offset(sc, 0, 0,
1109 (text_margin - icon_w) / 2,
1110 pm->main_y, Icon_Reverse_Cursor);
1112 else
1114 sc->getstringsize("<", &w, NULL);
1115 sc->putsxy(text_margin - w, pm->main_y, "<");
1119 if (state->len_utf8 - pm->leftpos > pm->max_chars_text)
1121 /* Draw nicer bitmap arrow if room, else settle for ">". */
1122 if (text_margin >= icon_w)
1124 screen_put_icon_with_offset(sc, 0, 0,
1125 sc_w - (text_margin + icon_w) / 2,
1126 pm->main_y, Icon_Cursor);
1128 else
1130 sc->putsxy(sc_w - text_margin, pm->main_y, ">");
1134 /* cursor */
1135 i = text_margin + pm->curpos * pm->text_w;
1137 if (state->cur_blink)
1138 sc->vline(i, pm->main_y, pm->main_y + pm->font_h - 1);
1140 if (state->hangul) /* draw underbar */
1141 sc->hline(i - pm->text_w, i, pm->main_y + pm->font_h - 1);
1143 #ifdef KBD_MODES
1144 if (pm->line_edit)
1146 sc->set_drawmode(DRMODE_COMPLEMENT);
1147 sc->fillrect(0, y + 2, sc_w, pm->font_h + 2);
1148 sc->set_drawmode(DRMODE_SOLID);
1150 #endif