keyboard: check length of the last line. avoid dividing by 0.
[kugel-rb.git] / apps / recorder / keyboard.c
blob060393c2696618170a99b3a7895d8dba8760aa80
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 unsigned short max_line_len;
99 int default_lines;
100 int nchars;
101 int font_w;
102 int font_h;
103 int text_w;
104 int curfont;
105 int main_y;
106 #ifdef HAVE_MORSE_INPUT
107 int old_main_y;
108 #endif
109 int max_chars;
110 int max_chars_text;
111 int lines;
112 int pages;
113 int keyboard_margin;
114 int curpos;
115 int leftpos;
116 int page;
117 int x;
118 int y;
119 #ifdef KBD_MODES
120 bool line_edit;
121 #endif
124 struct edit_state
126 char* text;
127 int buflen;
128 int len_utf8;
129 int editpos; /* Edit position on all screens */
130 bool cur_blink; /* Cursor on/off flag */
131 bool hangul;
132 unsigned short hlead, hvowel, htail;
133 #ifdef HAVE_MORSE_INPUT
134 bool morse_mode;
135 bool morse_reading;
136 unsigned char morse_code;
137 int morse_tick;
138 #endif
141 static struct keyboard_parameters kbd_param[NB_SCREENS];
142 static bool kbd_loaded = false;
144 #ifdef HAVE_MORSE_INPUT
145 /* FIXME: We should put this to a configuration file. */
146 static const char *morse_alphabets =
147 "abcdefghijklmnopqrstuvwxyz1234567890,.?-@ ";
148 static const unsigned char morse_codes[] = {
149 0x05,0x18,0x1a,0x0c,0x02,0x12,0x0e,0x10,0x04,0x17,0x0d,0x14,0x07,
150 0x06,0x0f,0x16,0x1d,0x0a,0x08,0x03,0x09,0x11,0x0b,0x19,0x1b,0x1c,
151 0x2f,0x27,0x23,0x21,0x20,0x30,0x38,0x3c,0x3e,0x3f,
152 0x73,0x55,0x4c,0x61,0x5a,0x80 };
153 #endif
155 /* Loads a custom keyboard into memory
156 call with NULL to reset keyboard */
157 int load_kbd(unsigned char* filename)
159 int fd, l;
160 int i, line_len, max_line_len;
161 unsigned char buf[4];
163 if (filename == NULL)
165 kbd_loaded = false;
166 return 0;
169 fd = open_utf8(filename, O_RDONLY|O_BINARY);
170 if (fd < 0)
171 return 1;
173 line_len = 0;
174 max_line_len = 1;
175 i = 0;
176 while (read(fd, buf, 1) == 1 && i < KBD_BUF_SIZE)
178 /* check how many bytes to read for this character */
179 static const unsigned char sizes[4] = { 0x80, 0xe0, 0xf0, 0xf5 };
180 size_t count;
181 unsigned short ch;
183 for (count = 0; count < ARRAYLEN(sizes); count++)
185 if (buf[0] < sizes[count])
186 break;
189 if (count >= ARRAYLEN(sizes))
190 continue; /* Invalid size. */
192 if (read(fd, &buf[1], count) != (ssize_t)count)
194 close(fd);
195 kbd_loaded = false;
196 return 1;
199 utf8decode(buf, &ch);
200 if (ch != 0xFEFF && ch != '\r') /* skip BOM & carriage returns */
202 FOR_NB_SCREENS(l)
203 kbd_param[l].kbd_buf[i] = ch;
204 i++;
205 if (ch == '\n')
207 if (max_line_len < line_len)
208 max_line_len = line_len;
209 line_len = 0;
211 else
212 line_len++;
216 close(fd);
217 kbd_loaded = true;
219 if (max_line_len < line_len)
220 max_line_len = line_len;
222 FOR_NB_SCREENS(l)
224 struct keyboard_parameters *pm = &kbd_param[l];
225 pm->nchars = i;
226 /* initialize parameters */
227 pm->x = pm->y = pm->page = 0;
228 pm->default_lines = 0;
229 pm->max_line_len = max_line_len;
232 return 0;
235 /* helper function to spell a char if voice UI is enabled */
236 static void kbd_spellchar(unsigned short c)
238 if (global_settings.talk_menu) /* voice UI? */
240 unsigned char tmp[5];
241 /* store char to pass to talk_spell */
242 unsigned char* utf8 = utf8encode(c, tmp);
243 *utf8 = 0;
245 if (c == ' ')
246 talk_id(VOICE_BLANK, false);
247 else
248 talk_spell(tmp, false);
252 #ifdef KBD_MODES
253 static void say_edit(void)
255 if (global_settings.talk_menu)
256 talk_id(VOICE_EDIT, false);
258 #endif
260 static void kbd_inschar(struct edit_state *state, unsigned short ch)
262 int i, j, len;
263 unsigned char tmp[4];
264 unsigned char* utf8;
266 len = strlen(state->text);
267 utf8 = utf8encode(ch, tmp);
268 j = (long)utf8 - (long)tmp;
270 if (len + j < state->buflen)
272 i = utf8seek(state->text, state->editpos);
273 utf8 = state->text + i;
274 memmove(utf8 + j, utf8, len - i + 1);
275 memcpy(utf8, tmp, j);
276 state->editpos++;
280 static void kbd_delchar(struct edit_state *state)
282 int i, j, len;
283 unsigned char* utf8;
285 if (state->editpos > 0)
287 state->editpos--;
288 len = strlen(state->text);
289 i = utf8seek(state->text, state->editpos);
290 utf8 = state->text + i;
291 j = utf8seek(utf8, 1);
292 memmove(utf8, utf8 + j, len - i - j + 1);
296 /* Lookup k value based on state of param (pm) */
297 static unsigned short get_kbd_ch(const struct keyboard_parameters *pm)
299 int k = (pm->page*pm->lines + pm->y)*pm->max_chars + pm->x;
300 return (k < pm->nchars)? pm->kbd_buf[k]: ' ';
303 static void kbd_calc_params(struct keyboard_parameters *pm,
304 struct screen *sc, struct edit_state *state);
305 static void kbd_draw_picker(struct keyboard_parameters *pm,
306 struct screen *sc, struct edit_state *state);
307 static void kbd_draw_edit_line(struct keyboard_parameters *pm,
308 struct screen *sc, struct edit_state *state);
310 int kbd_input(char* text, int buflen)
312 bool done = false;
313 #ifdef CPU_ARM
314 /* This seems to keep the sizes for ARM way down */
315 struct keyboard_parameters * volatile param = kbd_param;
316 #else
317 struct keyboard_parameters * const param = kbd_param;
318 #endif
319 struct edit_state state;
320 int l; /* screen loop variable */
321 unsigned short ch;
322 int ret = 0; /* assume success */
323 FOR_NB_SCREENS(l)
325 viewportmanager_theme_enable(l, false, NULL);
328 #ifdef HAVE_BUTTONBAR
329 struct gui_buttonbar buttonbar;
330 bool buttonbar_config = global_settings.buttonbar;
332 global_settings.buttonbar = true;
333 gui_buttonbar_init(&buttonbar);
334 gui_buttonbar_set_display(&buttonbar, &screens[SCREEN_MAIN]);
335 #endif
337 /* initialize state */
338 state.text = text;
339 state.buflen = buflen;
340 /* Initial edit position is after last character */
341 state.editpos = utf8length(state.text);
342 state.cur_blink = true;
343 #ifdef HAVE_MORSE_INPUT
344 state.morse_mode = global_settings.morse_input;
345 state.morse_reading = false;
346 #endif
347 state.hangul = false;
349 if (!kbd_loaded)
351 /* Copy default keyboard to buffer */
352 FOR_NB_SCREENS(l)
354 struct keyboard_parameters *pm = &param[l];
355 const unsigned char *p;
356 int i = 0;
358 #if LCD_WIDTH >= 160 && LCD_HEIGHT >= 96
359 struct screen *sc = &screens[l];
361 if (sc->getwidth() >= 160 && sc->getheight() >= 96)
363 p = "ABCDEFG abcdefg !?\" @#$%+'\n"
364 "HIJKLMN hijklmn 789 &_()-`\n"
365 "OPQRSTU opqrstu 456 §|{}/<\n"
366 "VWXYZ., vwxyz.,0123 ~=[]*>\n"
367 "ÀÁÂÃÄÅÆ ÌÍÎÏ ÈÉÊË ¢£¤¥¦§©®\n"
368 "àáâãäåæ ìíîï èéêë «»°ºª¹²³\n"
369 "ÓÒÔÕÖØ ÇÐÞÝß ÙÚÛÜ ¯±×÷¡¿µ·\n"
370 "òóôõöø çðþýÿ ùúûü ¼½¾¬¶¨:;";
372 pm->default_lines = 8;
373 pm->max_line_len = 26;
375 else
376 #endif /* LCD_WIDTH >= 160 && LCD_HEIGHT >= 96 */
378 p = "ABCDEFG !?\" @#$%+'\n"
379 "HIJKLMN 789 &_()-`\n"
380 "OPQRSTU 456 §|{}/<\n"
381 "VWXYZ.,0123 ~=[]*>\n"
383 "abcdefg ¢£¤¥¦§©®¬\n"
384 "hijklmn «»°ºª¹²³¶\n"
385 "opqrstu ¯±×÷¡¿µ·¨\n"
386 "vwxyz., :;¼½¾ \n"
388 "ÀÁÂÃÄÅÆ ÌÍÎÏ ÈÉÊË\n"
389 "àáâãäåæ ìíîï èéêë\n"
390 "ÓÒÔÕÖØ ÇÐÞÝß ÙÚÛÜ\n"
391 "òóôõöø çðþýÿ ùúûü";
393 pm->default_lines = 4;
394 pm->max_line_len = 18;
397 while (*p)
398 p = utf8decode(p, &pm->kbd_buf[i++]);
400 pm->nchars = i;
401 /* initialize parameters */
402 pm->x = pm->y = pm->page = 0;
404 kbd_loaded = true;
407 FOR_NB_SCREENS(l)
409 struct keyboard_parameters *pm = &param[l];
410 struct screen *sc = &screens[l];
411 kbd_calc_params(pm, sc, &state);
414 if (global_settings.talk_menu) /* voice UI? */
415 talk_spell(state.text, true); /* spell initial text */
417 while (!done)
419 /* These declarations are assigned to the screen on which the key
420 action occurred - pointers save a lot of space over array notation
421 when accessing the same array element countless times */
422 int button;
423 #if NB_SCREENS > 1
424 int button_screen;
425 #else
426 const int button_screen = 0;
427 #endif
428 struct keyboard_parameters *pm;
429 struct screen *sc;
431 state.len_utf8 = utf8length(state.text);
433 FOR_NB_SCREENS(l)
435 /* declare scoped pointers inside screen loops - hide the
436 declarations from previous block level */
437 struct keyboard_parameters *pm = &param[l];
438 struct screen *sc = &screens[l];
439 sc->clear_display();
440 kbd_draw_picker(pm, sc, &state);
441 kbd_draw_edit_line(pm, sc, &state);
444 state.cur_blink = !state.cur_blink;
446 #ifdef HAVE_BUTTONBAR
447 /* draw the button bar */
448 gui_buttonbar_set(&buttonbar, "Shift", "OK", "Del");
449 gui_buttonbar_draw(&buttonbar);
450 #endif
452 FOR_NB_SCREENS(l)
453 screens[l].update();
455 button = get_action(
456 #ifdef HAVE_MORSE_INPUT
457 state.morse_mode? CONTEXT_MORSE_INPUT:
458 #endif
459 CONTEXT_KEYBOARD, HZ/2);
460 #if NB_SCREENS > 1
461 button_screen = (get_action_statuscode(NULL) & ACTION_REMOTE) ? 1 : 0;
462 #endif
463 pm = &param[button_screen];
464 sc = &screens[button_screen];
466 #if defined(KBD_MODES) || defined(HAVE_MORSE_INPUT)
467 /* Remap some buttons to allow to move
468 * cursor in line edit mode and morse mode. */
469 #if defined(KBD_MODES) && defined(HAVE_MORSE_INPUT)
470 if (pm->line_edit || state.morse_mode)
471 #elif defined(KBD_MODES)
472 if (pm->line_edit)
473 #else /* defined(HAVE_MORSE_INPUT) */
474 if (state.morse_mode)
475 #endif
477 if (button == ACTION_KBD_LEFT)
478 button = ACTION_KBD_CURSOR_LEFT;
479 if (button == ACTION_KBD_RIGHT)
480 button = ACTION_KBD_CURSOR_RIGHT;
481 #ifdef KBD_MODES
482 /* select doubles as backspace in line_edit */
483 if (pm->line_edit && button == ACTION_KBD_SELECT)
484 button = ACTION_KBD_BACKSPACE;
485 #endif
487 #endif /* defined(KBD_MODES) || defined(HAVE_MORSE_INPUT) */
489 switch ( button )
491 case ACTION_KBD_DONE:
492 /* accepts what was entered and continues */
493 ret = 0;
494 done = true;
495 break;
497 case ACTION_KBD_ABORT:
498 ret = -1;
499 done = true;
500 break;
502 case ACTION_KBD_PAGE_FLIP:
503 #ifdef HAVE_MORSE_INPUT
504 if (state.morse_mode)
505 break;
506 #endif
507 if (++pm->page >= pm->pages)
508 pm->page = 0;
510 ch = get_kbd_ch(pm);
511 kbd_spellchar(ch);
512 break;
514 #if defined(HAVE_MORSE_INPUT) && defined(KBD_TOGGLE_INPUT)
515 case ACTION_KBD_MORSE_INPUT:
516 state.morse_mode = !state.morse_mode;
518 FOR_NB_SCREENS(l)
520 struct keyboard_parameters *pm = &param[l];
521 int y = pm->main_y;
522 pm->main_y = pm->old_main_y;
523 pm->old_main_y = y;
525 /* FIXME: We should talk something like Morse mode.. */
526 break;
527 #endif /* HAVE_MORSE_INPUT && KBD_TOGGLE_INPUT */
529 case ACTION_KBD_RIGHT:
530 if (++pm->x >= pm->max_chars)
532 #ifndef KBD_PAGE_FLIP
533 /* no dedicated flip key - flip page on wrap */
534 if (++pm->page >= pm->pages)
535 pm->page = 0;
536 #endif
537 pm->x = 0;
540 ch = get_kbd_ch(pm);
541 kbd_spellchar(ch);
542 break;
544 case ACTION_KBD_LEFT:
545 if (--pm->x < 0)
547 #ifndef KBD_PAGE_FLIP
548 /* no dedicated flip key - flip page on wrap */
549 if (--pm->page < 0)
550 pm->page = pm->pages - 1;
551 #endif
552 pm->x = pm->max_chars - 1;
555 ch = get_kbd_ch(pm);
556 kbd_spellchar(ch);
557 break;
559 case ACTION_KBD_DOWN:
560 #ifdef HAVE_MORSE_INPUT
561 if (state.morse_mode)
563 #ifdef KBD_MODES
564 pm->line_edit = !pm->line_edit;
565 if (pm->line_edit)
566 say_edit();
567 #endif
568 break;
570 #endif /* HAVE_MORSE_INPUT */
571 #ifdef KBD_MODES
572 if (pm->line_edit)
574 pm->y = 0;
575 pm->line_edit = false;
577 else if (++pm->y >= pm->lines)
579 pm->line_edit = true;
580 say_edit();
582 #else
583 if (++pm->y >= pm->lines)
584 pm->y = 0;
585 #endif
586 #ifdef KBD_MODES
587 if (!pm->line_edit)
588 #endif
590 ch = get_kbd_ch(pm);
591 kbd_spellchar(ch);
593 break;
595 case ACTION_KBD_UP:
596 #ifdef HAVE_MORSE_INPUT
597 if (state.morse_mode)
599 #ifdef KBD_MODES
600 pm->line_edit = !pm->line_edit;
601 if (pm->line_edit)
602 say_edit();
603 #endif
604 break;
606 #endif /* HAVE_MORSE_INPUT */
607 #ifdef KBD_MODES
608 if (pm->line_edit)
610 pm->y = pm->lines - 1;
611 pm->line_edit = false;
613 else if (--pm->y < 0)
615 pm->line_edit = true;
616 say_edit();
618 #else
619 if (--pm->y < 0)
620 pm->y = pm->lines - 1;
621 #endif
622 #ifdef KBD_MODES
623 if (!pm->line_edit)
624 #endif
626 ch = get_kbd_ch(pm);
627 kbd_spellchar(ch);
629 break;
631 #ifdef HAVE_MORSE_INPUT
632 case ACTION_KBD_MORSE_SELECT:
633 if (state.morse_mode && state.morse_reading)
635 state.morse_code <<= 1;
636 if ((current_tick - state.morse_tick) > HZ/5)
637 state.morse_code |= 0x01;
639 break;
640 #endif /* HAVE_MORSE_INPUT */
642 case ACTION_KBD_SELECT:
643 #ifdef HAVE_MORSE_INPUT
644 if (state.morse_mode)
646 state.morse_tick = current_tick;
648 if (!state.morse_reading)
650 state.morse_reading = true;
651 state.morse_code = 1;
654 else
655 #endif /* HAVE_MORSE_INPUT */
657 /* inserts the selected char */
658 /* find input char */
659 ch = get_kbd_ch(pm);
661 /* check for hangul input */
662 if (ch >= 0x3131 && ch <= 0x3163)
664 unsigned short tmp;
666 if (!state.hangul)
668 state.hlead = state.hvowel = state.htail = 0;
669 state.hangul = true;
672 if (!state.hvowel)
674 state.hvowel = ch;
676 else if (!state.htail)
678 state.htail = ch;
680 else
682 /* previous hangul complete */
683 /* check whether tail is actually lead of next char */
684 tmp = hangul_join(state.htail, ch, 0);
686 if (tmp != 0xfffd)
688 tmp = hangul_join(state.hlead, state.hvowel, 0);
689 kbd_delchar(&state);
690 kbd_inschar(&state, tmp);
691 /* insert dummy char */
692 kbd_inschar(&state, ' ');
693 state.hlead = state.htail;
694 state.hvowel = ch;
695 state.htail = 0;
697 else
699 state.hvowel = state.htail = 0;
700 state.hlead = ch;
704 /* combine into hangul */
705 tmp = hangul_join(state.hlead, state.hvowel, state.htail);
707 if (tmp != 0xfffd)
709 kbd_delchar(&state);
710 ch = tmp;
712 else
714 state.hvowel = state.htail = 0;
715 state.hlead = ch;
718 else
720 state.hangul = false;
723 /* insert char */
724 kbd_inschar(&state, ch);
726 if (global_settings.talk_menu) /* voice UI? */
727 talk_spell(state.text, false); /* speak revised text */
729 break;
731 case ACTION_KBD_BACKSPACE:
732 if (state.hangul)
734 if (state.htail)
735 state.htail = 0;
736 else if (state.hvowel)
737 state.hvowel = 0;
738 else
739 state.hangul = false;
742 kbd_delchar(&state);
744 if (state.hangul)
746 if (state.hvowel)
747 ch = hangul_join(state.hlead, state.hvowel, state.htail);
748 else
749 ch = state.hlead;
750 kbd_inschar(&state, ch);
753 if (global_settings.talk_menu) /* voice UI? */
754 talk_spell(state.text, false); /* speak revised text */
755 break;
757 case ACTION_KBD_CURSOR_RIGHT:
758 state.hangul = false;
760 if (state.editpos < state.len_utf8)
762 int c = utf8seek(state.text, ++state.editpos);
763 kbd_spellchar(state.text[c]);
765 #if CONFIG_CODEC == SWCODEC
766 else if (global_settings.talk_menu)
767 pcmbuf_beep(1000, 150, 1500);
768 #endif
769 break;
771 case ACTION_KBD_CURSOR_LEFT:
772 state.hangul = false;
774 if (state.editpos > 0)
776 int c = utf8seek(state.text, --state.editpos);
777 kbd_spellchar(state.text[c]);
779 #if CONFIG_CODEC == SWCODEC
780 else if (global_settings.talk_menu)
781 pcmbuf_beep(1000, 150, 1500);
782 #endif
783 break;
785 case ACTION_NONE:
786 #ifdef HAVE_MORSE_INPUT
787 if (state.morse_reading)
789 int j;
790 logf("Morse: 0x%02x", state.morse_code);
791 state.morse_reading = false;
793 for (j = 0; morse_alphabets[j] != '\0'; j++)
795 if (morse_codes[j] == state.morse_code)
796 break ;
799 if (morse_alphabets[j] == '\0')
801 logf("Morse code not found");
802 break ;
805 /* turn off hangul input */
806 state.hangul = false;
807 kbd_inschar(&state, morse_alphabets[j]);
809 if (global_settings.talk_menu) /* voice UI? */
810 talk_spell(state.text, false); /* speak revised text */
812 #endif /* HAVE_MORSE_INPUT */
813 break;
815 default:
816 if (default_event_handler(button) == SYS_USB_CONNECTED)
818 FOR_NB_SCREENS(l)
819 screens[l].setfont(FONT_SYSFIXED);
821 break;
823 } /* end switch */
825 if (button != ACTION_NONE)
827 state.cur_blink = true;
831 #ifdef HAVE_BUTTONBAR
832 global_settings.buttonbar = buttonbar_config;
833 #endif
835 if (ret < 0)
836 splash(HZ/2, ID2P(LANG_CANCEL));
838 #if defined(HAVE_MORSE_INPUT) && defined(KBD_TOGGLE_INPUT)
839 if (global_settings.morse_input != state.morse_mode)
841 global_settings.morse_input = state.morse_mode;
842 settings_save();
844 #endif /* HAVE_MORSE_INPUT && KBD_TOGGLE_INPUT */
846 FOR_NB_SCREENS(l)
848 screens[l].setfont(FONT_UI);
849 viewportmanager_theme_undo(l, false);
851 return ret;
854 static void kbd_calc_params(struct keyboard_parameters *pm,
855 struct screen *sc, struct edit_state *state)
857 struct font* font;
858 const unsigned char *p;
859 unsigned short ch;
860 int icon_w, sc_w, sc_h, w;
861 int i, total_lines;
863 pm->curfont = pm->default_lines ? FONT_SYSFIXED : FONT_UI;
864 font = font_get(pm->curfont);
865 pm->font_h = font->height;
867 /* check if FONT_UI fits the screen */
868 if (2*pm->font_h + 3 + BUTTONBAR_HEIGHT > sc->getheight())
870 pm->curfont = FONT_SYSFIXED;
871 font = font_get(FONT_SYSFIXED);
872 pm->font_h = font->height;
875 /* find max width of keyboard glyphs.
876 * since we're going to be adding spaces,
877 * max width is at least their width */
878 pm->font_w = font_get_width(font, ' ');
879 for (i = 0; i < pm->nchars; i++)
881 if (pm->kbd_buf[i] != '\n')
883 w = font_get_width(font, pm->kbd_buf[i]);
884 if (pm->font_w < w)
885 pm->font_w = w;
889 /* Find max width for text string */
890 pm->text_w = pm->font_w;
891 p = state->text;
892 while (*p)
894 p = utf8decode(p, &ch);
895 w = font_get_width(font, ch);
896 if (pm->text_w < w)
897 pm->text_w = w;
900 /* calculate how many characters to put in a row. */
901 icon_w = get_icon_width(sc->screen_type);
902 sc_w = sc->getwidth();
903 if (pm->font_w < sc_w / pm->max_line_len)
904 pm->font_w = sc_w / pm->max_line_len;
905 pm->max_chars = sc_w / pm->font_w;
906 pm->max_chars_text = (sc_w - icon_w * 2 - 2) / pm->text_w;
907 if (pm->max_chars_text < 3 && icon_w > pm->text_w)
908 pm->max_chars_text = sc_w / pm->text_w - 2;
911 i = 0;
912 /* Pad lines with spaces */
913 while (i < pm->nchars)
915 if (pm->kbd_buf[i] == '\n')
917 int k = pm->max_chars - i % ( pm->max_chars ) - 1;
918 int j;
920 if (k == pm->max_chars - 1)
922 pm->nchars--;
924 for (j = i; j < pm->nchars; j++)
926 pm->kbd_buf[j] = pm->kbd_buf[j + 1];
929 else
931 if (pm->nchars + k - 1 >= KBD_BUF_SIZE)
932 { /* We don't want to overflow the buffer */
933 k = KBD_BUF_SIZE - pm->nchars;
936 for (j = pm->nchars + k - 1; j > i + k; j--)
938 pm->kbd_buf[j] = pm->kbd_buf[j-k];
941 pm->nchars += k;
942 k++;
944 while (k--)
946 pm->kbd_buf[i++] = ' ';
950 else
952 i++;
955 if (pm->nchars == 0)
956 pm->kbd_buf[pm->nchars++] = ' ';
958 /* calculate pm->pages and pm->lines */
959 sc_h = sc->getheight();
960 pm->lines = (sc_h - BUTTONBAR_HEIGHT) / pm->font_h - 1;
962 if (pm->default_lines && pm->lines > pm->default_lines)
963 pm->lines = pm->default_lines;
965 pm->keyboard_margin = sc_h - BUTTONBAR_HEIGHT
966 - (pm->lines+1)*pm->font_h;
968 if (pm->keyboard_margin < 3 && pm->lines > 1)
970 pm->lines--;
971 pm->keyboard_margin += pm->font_h;
974 if (pm->keyboard_margin > DEFAULT_MARGIN)
975 pm->keyboard_margin = DEFAULT_MARGIN;
977 total_lines = (pm->nchars + pm->max_chars - 1) / pm->max_chars;
978 pm->pages = (total_lines + pm->lines - 1) / pm->lines;
979 pm->lines = (total_lines + pm->pages - 1) / pm->pages;
981 pm->main_y = pm->font_h*pm->lines + pm->keyboard_margin;
982 pm->keyboard_margin -= pm->keyboard_margin/2;
984 #ifdef HAVE_MORSE_INPUT
985 pm->old_main_y = sc_h - pm->font_h - BUTTONBAR_HEIGHT;
986 if (state->morse_mode)
988 int y = pm->main_y;
989 pm->main_y = pm->old_main_y;
990 pm->old_main_y = y;
992 #endif
995 static void kbd_draw_picker(struct keyboard_parameters *pm,
996 struct screen *sc, struct edit_state *state)
998 char outline[8];
999 #ifdef HAVE_MORSE_INPUT
1000 if (state->morse_mode)
1002 const int w = 6, h = 8; /* sysfixed font width, height */
1003 int i, j, x, y;
1004 int sc_w = sc->getwidth(), sc_h = pm->main_y - pm->keyboard_margin - 1;
1006 /* Draw morse code screen with sysfont */
1007 sc->setfont(FONT_SYSFIXED);
1008 x = 0;
1009 y = 0;
1010 outline[1] = '\0';
1012 /* Draw morse code table with code descriptions. */
1013 for (i = 0; morse_alphabets[i] != '\0'; i++)
1015 int morse_code;
1017 outline[0] = morse_alphabets[i];
1018 sc->putsxy(x, y, outline);
1020 morse_code = morse_codes[i];
1021 for (j = 0; morse_code > 0x01; morse_code >>= 1)
1022 j++;
1024 x += w + 3 + j*4;
1025 morse_code = morse_codes[i];
1026 for (; morse_code > 0x01; morse_code >>= 1)
1028 x -= 4;
1029 if (morse_code & 0x01)
1030 sc->fillrect(x, y + 2, 3, 4);
1031 else
1032 sc->fillrect(x, y + 3, 1, 2);
1035 x += w*5 - 3;
1036 if (x + w*6 >= sc_w)
1038 x = 0;
1039 y += h;
1040 if (y + h >= sc_h)
1041 break;
1045 else
1046 #else
1047 (void) state;
1048 #endif /* HAVE_MORSE_INPUT */
1050 /* draw page */
1051 int i, j, k;
1053 sc->setfont(pm->curfont);
1055 k = pm->page*pm->max_chars*pm->lines;
1057 for (i = j = 0; k < pm->nchars; k++)
1059 int w;
1060 unsigned char *utf8;
1061 utf8 = utf8encode(pm->kbd_buf[k], outline);
1062 *utf8 = 0;
1064 sc->getstringsize(outline, &w, NULL);
1065 sc->putsxy(i*pm->font_w + (pm->font_w-w) / 2,
1066 j*pm->font_h, outline);
1068 if (++i >= pm->max_chars)
1070 i = 0;
1071 if (++j >= pm->lines)
1072 break;
1076 #ifdef KBD_MODES
1077 if (!pm->line_edit)
1078 #endif
1080 /* highlight the key that has focus */
1081 sc->set_drawmode(DRMODE_COMPLEMENT);
1082 sc->fillrect(pm->font_w*pm->x, pm->font_h*pm->y,
1083 pm->font_w, pm->font_h);
1084 sc->set_drawmode(DRMODE_SOLID);
1089 static void kbd_draw_edit_line(struct keyboard_parameters *pm,
1090 struct screen *sc, struct edit_state *state)
1092 char outline[8];
1093 unsigned char *utf8;
1094 int i = 0, j = 0, icon_w, w;
1095 int sc_w = sc->getwidth();
1096 int y = pm->main_y - pm->keyboard_margin;
1097 int text_margin = (sc_w - pm->text_w * pm->max_chars_text) / 2;
1099 /* Clear text area one pixel above separator line so any overdraw
1100 doesn't collide */
1101 screen_clear_area(sc, 0, y - 1, sc_w, pm->font_h + 6);
1103 sc->hline(0, sc_w - 1, y);
1105 /* write out the text */
1106 sc->setfont(pm->curfont);
1108 pm->leftpos = MAX(0, MIN(state->len_utf8, state->editpos + 2)
1109 - pm->max_chars_text);
1110 pm->curpos = state->editpos - pm->leftpos;
1111 utf8 = state->text + utf8seek(state->text, pm->leftpos);
1113 while (*utf8 && i < pm->max_chars_text)
1115 j = utf8seek(utf8, 1);
1116 strlcpy(outline, utf8, j+1);
1117 sc->getstringsize(outline, &w, NULL);
1118 sc->putsxy(text_margin + i*pm->text_w + (pm->text_w-w)/2,
1119 pm->main_y, outline);
1120 utf8 += j;
1121 i++;
1124 icon_w = get_icon_width(sc->screen_type);
1125 if (pm->leftpos > 0)
1127 /* Draw nicer bitmap arrow if room, else settle for "<". */
1128 if (text_margin >= icon_w)
1130 screen_put_icon_with_offset(sc, 0, 0,
1131 (text_margin - icon_w) / 2,
1132 pm->main_y, Icon_Reverse_Cursor);
1134 else
1136 sc->getstringsize("<", &w, NULL);
1137 sc->putsxy(text_margin - w, pm->main_y, "<");
1141 if (state->len_utf8 - pm->leftpos > pm->max_chars_text)
1143 /* Draw nicer bitmap arrow if room, else settle for ">". */
1144 if (text_margin >= icon_w)
1146 screen_put_icon_with_offset(sc, 0, 0,
1147 sc_w - (text_margin + icon_w) / 2,
1148 pm->main_y, Icon_Cursor);
1150 else
1152 sc->putsxy(sc_w - text_margin, pm->main_y, ">");
1156 /* cursor */
1157 i = text_margin + pm->curpos * pm->text_w;
1159 if (state->cur_blink)
1160 sc->vline(i, pm->main_y, pm->main_y + pm->font_h - 1);
1162 if (state->hangul) /* draw underbar */
1163 sc->hline(i - pm->text_w, i, pm->main_y + pm->font_h - 1);
1165 #ifdef KBD_MODES
1166 if (pm->line_edit)
1168 sc->set_drawmode(DRMODE_COMPLEMENT);
1169 sc->fillrect(0, y + 2, sc_w, pm->font_h + 2);
1170 sc->set_drawmode(DRMODE_SOLID);
1172 #endif