lcd-m6sp.c: remove \r
[kugel-rb.git] / apps / recorder / keyboard.c
blobe8894d179bdb7639486fab04abc5e9721ec89e2c
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-extra.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 == IRIVER_H100_PAD) \
51 || (CONFIG_KEYPAD == IRIVER_H300_PAD) \
52 || (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
53 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
54 || (CONFIG_KEYPAD == IPOD_4G_PAD) \
55 || (CONFIG_KEYPAD == IRIVER_H10_PAD) \
56 || (CONFIG_KEYPAD == GIGABEAT_PAD) \
57 || (CONFIG_KEYPAD == GIGABEAT_S_PAD) \
58 || (CONFIG_KEYPAD == MROBE100_PAD) \
59 || (CONFIG_KEYPAD == SANSA_E200_PAD) \
60 || (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD) \
61 || (CONFIG_KEYPAD == PHILIPS_SA9200_PAD) \
62 || (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
63 /* certain key combos toggle input mode between keyboard input and Morse input */
64 #define KBD_TOGGLE_INPUT
65 #endif
67 #define CHANGED_PICKER 1
68 #define CHANGED_CURSOR 2
69 #define CHANGED_TEXT 3
71 struct keyboard_parameters
73 unsigned short kbd_buf[KBD_BUF_SIZE];
74 unsigned short max_line_len;
75 int default_lines;
76 int nchars;
77 int font_w;
78 int font_h;
79 int text_w;
80 int curfont;
81 int main_y;
82 #ifdef HAVE_MORSE_INPUT
83 int old_main_y;
84 #endif
85 int max_chars;
86 int max_chars_text;
87 int lines;
88 int pages;
89 int keyboard_margin;
90 int curpos;
91 int leftpos;
92 int page;
93 int x;
94 int y;
95 bool line_edit;
98 struct edit_state
100 char* text;
101 int buflen;
102 int len_utf8;
103 int editpos; /* Edit position on all screens */
104 bool cur_blink; /* Cursor on/off flag */
105 bool hangul;
106 unsigned short hlead, hvowel, htail;
107 #ifdef HAVE_MORSE_INPUT
108 bool morse_mode;
109 bool morse_reading;
110 unsigned char morse_code;
111 int morse_tick;
112 #endif
113 int changed;
116 static struct keyboard_parameters kbd_param[NB_SCREENS];
117 static bool kbd_loaded = false;
119 #ifdef HAVE_MORSE_INPUT
120 /* FIXME: We should put this to a configuration file. */
121 static const char *morse_alphabets =
122 "abcdefghijklmnopqrstuvwxyz1234567890,.?-@ ";
123 static const unsigned char morse_codes[] = {
124 0x05,0x18,0x1a,0x0c,0x02,0x12,0x0e,0x10,0x04,0x17,0x0d,0x14,0x07,
125 0x06,0x0f,0x16,0x1d,0x0a,0x08,0x03,0x09,0x11,0x0b,0x19,0x1b,0x1c,
126 0x2f,0x27,0x23,0x21,0x20,0x30,0x38,0x3c,0x3e,0x3f,
127 0x73,0x55,0x4c,0x61,0x5a,0x80 };
128 #endif
130 /* Loads a custom keyboard into memory
131 call with NULL to reset keyboard */
132 int load_kbd(unsigned char* filename)
134 int fd, l;
135 int i, line_len, max_line_len;
136 unsigned char buf[4];
138 if (filename == NULL)
140 kbd_loaded = false;
141 return 0;
144 fd = open_utf8(filename, O_RDONLY|O_BINARY);
145 if (fd < 0)
146 return 1;
148 line_len = 0;
149 max_line_len = 1;
150 i = 0;
151 while (read(fd, buf, 1) == 1 && i < KBD_BUF_SIZE)
153 /* check how many bytes to read for this character */
154 static const unsigned char sizes[4] = { 0x80, 0xe0, 0xf0, 0xf5 };
155 size_t count;
156 unsigned short ch;
158 for (count = 0; count < ARRAYLEN(sizes); count++)
160 if (buf[0] < sizes[count])
161 break;
164 if (count >= ARRAYLEN(sizes))
165 continue; /* Invalid size. */
167 if (read(fd, &buf[1], count) != (ssize_t)count)
169 close(fd);
170 kbd_loaded = false;
171 return 1;
174 utf8decode(buf, &ch);
175 if (ch != 0xFEFF && ch != '\r') /* skip BOM & carriage returns */
177 FOR_NB_SCREENS(l)
178 kbd_param[l].kbd_buf[i] = ch;
179 i++;
180 if (ch == '\n')
182 if (max_line_len < line_len)
183 max_line_len = line_len;
184 line_len = 0;
186 else
187 line_len++;
191 close(fd);
192 kbd_loaded = true;
194 if (max_line_len < line_len)
195 max_line_len = line_len;
197 FOR_NB_SCREENS(l)
199 struct keyboard_parameters *pm = &kbd_param[l];
200 pm->nchars = i;
201 /* initialize parameters */
202 pm->x = pm->y = pm->page = 0;
203 pm->default_lines = 0;
204 pm->max_line_len = max_line_len;
207 return 0;
210 /* helper function to spell a char */
211 static void kbd_spellchar(unsigned short c)
213 unsigned char tmp[5];
214 /* store char to pass to talk_spell */
215 unsigned char* utf8 = utf8encode(c, tmp);
216 *utf8 = 0;
218 if (c == ' ')
219 talk_id(VOICE_BLANK, false);
220 else
221 talk_spell(tmp, false);
224 static void kbd_inschar(struct edit_state *state, unsigned short ch)
226 int i, j, len;
227 unsigned char tmp[4];
228 unsigned char* utf8;
230 len = strlen(state->text);
231 utf8 = utf8encode(ch, tmp);
232 j = (long)utf8 - (long)tmp;
234 if (len + j < state->buflen)
236 i = utf8seek(state->text, state->editpos);
237 utf8 = state->text + i;
238 memmove(utf8 + j, utf8, len - i + 1);
239 memcpy(utf8, tmp, j);
240 state->editpos++;
241 state->changed = CHANGED_TEXT;
245 static void kbd_delchar(struct edit_state *state)
247 int i, j, len;
248 unsigned char* utf8;
250 if (state->editpos > 0)
252 state->editpos--;
253 len = strlen(state->text);
254 i = utf8seek(state->text, state->editpos);
255 utf8 = state->text + i;
256 j = utf8seek(utf8, 1);
257 memmove(utf8, utf8 + j, len - i - j + 1);
258 state->changed = CHANGED_TEXT;
262 /* Lookup k value based on state of param (pm) */
263 static unsigned short get_kbd_ch(const struct keyboard_parameters *pm)
265 int k = (pm->page*pm->lines + pm->y)*pm->max_chars + pm->x;
266 return (k < pm->nchars)? pm->kbd_buf[k]: ' ';
269 static void kbd_calc_params(struct keyboard_parameters *pm,
270 struct screen *sc, struct edit_state *state);
271 static void kbd_draw_picker(struct keyboard_parameters *pm,
272 struct screen *sc, struct edit_state *state);
273 static void kbd_draw_edit_line(struct keyboard_parameters *pm,
274 struct screen *sc, struct edit_state *state);
275 static void kbd_insert_selected(struct keyboard_parameters *pm,
276 struct edit_state *state);
277 static void kbd_backspace(struct edit_state *state);
278 static void kbd_move_cursor(struct edit_state *state, int dir);
279 static void kbd_move_picker_horizontal(struct keyboard_parameters *pm,
280 struct edit_state *state, int dir);
281 static void kbd_move_picker_vertical(struct keyboard_parameters *pm,
282 struct edit_state *state, int dir);
284 int kbd_input(char* text, int buflen)
286 bool done = false;
287 #ifdef CPU_ARM
288 /* This seems to keep the sizes for ARM way down */
289 struct keyboard_parameters * volatile param = kbd_param;
290 #else
291 struct keyboard_parameters * const param = kbd_param;
292 #endif
293 struct edit_state state;
294 int l; /* screen loop variable */
295 unsigned short ch;
296 int ret = 0; /* assume success */
297 FOR_NB_SCREENS(l)
299 viewportmanager_theme_enable(l, false, NULL);
302 #ifdef HAVE_BUTTONBAR
303 struct gui_buttonbar buttonbar;
304 bool buttonbar_config = global_settings.buttonbar;
306 global_settings.buttonbar = true;
307 gui_buttonbar_init(&buttonbar);
308 gui_buttonbar_set_display(&buttonbar, &screens[SCREEN_MAIN]);
309 #endif
311 /* initialize state */
312 state.text = text;
313 state.buflen = buflen;
314 /* Initial edit position is after last character */
315 state.editpos = utf8length(state.text);
316 state.cur_blink = true;
317 #ifdef HAVE_MORSE_INPUT
318 state.morse_mode = global_settings.morse_input;
319 state.morse_reading = false;
320 #endif
321 state.hangul = false;
322 state.changed = 0;
324 if (!kbd_loaded)
326 /* Copy default keyboard to buffer */
327 FOR_NB_SCREENS(l)
329 struct keyboard_parameters *pm = &param[l];
330 const unsigned char *p;
331 int i = 0;
333 #if LCD_WIDTH >= 160 && LCD_HEIGHT >= 96
334 struct screen *sc = &screens[l];
336 if (sc->getwidth() >= 160 && sc->getheight() >= 96)
338 p = "ABCDEFG abcdefg !?\" @#$%+'\n"
339 "HIJKLMN hijklmn 789 &_()-`\n"
340 "OPQRSTU opqrstu 456 §|{}/<\n"
341 "VWXYZ., vwxyz.,0123 ~=[]*>\n"
342 "ÀÁÂÃÄÅÆ ÌÍÎÏ ÈÉÊË ¢£¤¥¦§©®\n"
343 "àáâãäåæ ìíîï èéêë «»°ºª¹²³\n"
344 "ÓÒÔÕÖØ ÇÐÞÝß ÙÚÛÜ ¯±×÷¡¿µ·\n"
345 "òóôõöø çðþýÿ ùúûü ¼½¾¬¶¨:;";
347 pm->default_lines = 8;
348 pm->max_line_len = 26;
350 else
351 #endif /* LCD_WIDTH >= 160 && LCD_HEIGHT >= 96 */
353 p = "ABCDEFG !?\" @#$%+'\n"
354 "HIJKLMN 789 &_()-`\n"
355 "OPQRSTU 456 §|{}/<\n"
356 "VWXYZ.,0123 ~=[]*>\n"
358 "abcdefg ¢£¤¥¦§©®¬\n"
359 "hijklmn «»°ºª¹²³¶\n"
360 "opqrstu ¯±×÷¡¿µ·¨\n"
361 "vwxyz., :;¼½¾ \n"
363 "ÀÁÂÃÄÅÆ ÌÍÎÏ ÈÉÊË\n"
364 "àáâãäåæ ìíîï èéêë\n"
365 "ÓÒÔÕÖØ ÇÐÞÝß ÙÚÛÜ\n"
366 "òóôõöø çðþýÿ ùúûü";
368 pm->default_lines = 4;
369 pm->max_line_len = 18;
372 while (*p)
373 p = utf8decode(p, &pm->kbd_buf[i++]);
375 pm->nchars = i;
376 /* initialize parameters */
377 pm->x = pm->y = pm->page = 0;
379 kbd_loaded = true;
382 FOR_NB_SCREENS(l)
384 struct keyboard_parameters *pm = &param[l];
385 struct screen *sc = &screens[l];
386 kbd_calc_params(pm, sc, &state);
389 if (global_settings.talk_menu) /* voice UI? */
390 talk_spell(state.text, true); /* spell initial text */
392 while (!done)
394 /* These declarations are assigned to the screen on which the key
395 action occurred - pointers save a lot of space over array notation
396 when accessing the same array element countless times */
397 int button;
398 #if NB_SCREENS > 1
399 int button_screen;
400 #else
401 const int button_screen = 0;
402 #endif
403 struct keyboard_parameters *pm;
404 struct screen *sc;
406 state.len_utf8 = utf8length(state.text);
408 FOR_NB_SCREENS(l)
410 /* declare scoped pointers inside screen loops - hide the
411 declarations from previous block level */
412 struct keyboard_parameters *pm = &param[l];
413 struct screen *sc = &screens[l];
414 sc->clear_display();
415 kbd_draw_picker(pm, sc, &state);
416 kbd_draw_edit_line(pm, sc, &state);
419 #ifdef HAVE_BUTTONBAR
420 /* draw the button bar */
421 gui_buttonbar_set(&buttonbar, "Shift", "OK", "Del");
422 gui_buttonbar_draw(&buttonbar);
423 #endif
425 FOR_NB_SCREENS(l)
426 screens[l].update();
428 state.cur_blink = !state.cur_blink;
430 button = get_action(
431 #ifdef HAVE_MORSE_INPUT
432 state.morse_mode? CONTEXT_MORSE_INPUT:
433 #endif
434 CONTEXT_KEYBOARD, HZ/2);
435 #if NB_SCREENS > 1
436 button_screen = (get_action_statuscode(NULL) & ACTION_REMOTE) ? 1 : 0;
437 #endif
438 pm = &param[button_screen];
439 sc = &screens[button_screen];
441 /* Remap some buttons to allow to move
442 * cursor in line edit mode and morse mode. */
443 if (pm->line_edit
444 #ifdef HAVE_MORSE_INPUT
445 || state.morse_mode
446 #endif /* HAVE_MORSE_INPUT */
449 if (button == ACTION_KBD_LEFT)
450 button = ACTION_KBD_CURSOR_LEFT;
451 if (button == ACTION_KBD_RIGHT)
452 button = ACTION_KBD_CURSOR_RIGHT;
455 switch ( button )
457 case ACTION_KBD_DONE:
458 /* accepts what was entered and continues */
459 ret = 0;
460 done = true;
461 break;
463 case ACTION_KBD_ABORT:
464 ret = -1;
465 done = true;
466 break;
468 case ACTION_KBD_PAGE_FLIP:
469 #ifdef HAVE_MORSE_INPUT
470 if (state.morse_mode)
471 break;
472 #endif
473 if (++pm->page >= pm->pages)
474 pm->page = 0;
476 state.changed = CHANGED_PICKER;
477 break;
479 case ACTION_KBD_RIGHT:
480 kbd_move_picker_horizontal(pm, &state, 1);
481 break;
483 case ACTION_KBD_LEFT:
484 kbd_move_picker_horizontal(pm, &state, -1);
485 break;
487 case ACTION_KBD_DOWN:
488 kbd_move_picker_vertical(pm, &state, 1);
489 break;
491 case ACTION_KBD_UP:
492 kbd_move_picker_vertical(pm, &state, -1);
493 break;
495 #ifdef HAVE_MORSE_INPUT
496 #ifdef KBD_TOGGLE_INPUT
497 case ACTION_KBD_MORSE_INPUT:
498 state.morse_mode = !state.morse_mode;
499 state.changed = CHANGED_PICKER;
501 FOR_NB_SCREENS(l)
503 struct keyboard_parameters *pm = &param[l];
504 int y = pm->main_y;
505 pm->main_y = pm->old_main_y;
506 pm->old_main_y = y;
508 break;
509 #endif /* KBD_TOGGLE_INPUT */
511 case ACTION_KBD_MORSE_SELECT:
512 if (state.morse_mode && state.morse_reading)
514 state.morse_code <<= 1;
515 if ((current_tick - state.morse_tick) > HZ/5)
516 state.morse_code |= 0x01;
518 break;
519 #endif /* HAVE_MORSE_INPUT */
521 case ACTION_KBD_SELECT:
522 /* select doubles as backspace in line_edit */
523 if (pm->line_edit)
524 kbd_backspace(&state);
525 else
526 #ifdef HAVE_MORSE_INPUT
527 if (state.morse_mode)
529 state.morse_tick = current_tick;
531 if (!state.morse_reading)
533 state.morse_reading = true;
534 state.morse_code = 1;
537 else
538 #endif /* HAVE_MORSE_INPUT */
539 kbd_insert_selected(pm, &state);
540 break;
542 case ACTION_KBD_BACKSPACE:
543 kbd_backspace(&state);
544 break;
546 case ACTION_KBD_CURSOR_RIGHT:
547 kbd_move_cursor(&state, 1);
548 break;
550 case ACTION_KBD_CURSOR_LEFT:
551 kbd_move_cursor(&state, -1);
552 break;
554 case ACTION_NONE:
555 #ifdef HAVE_MORSE_INPUT
556 if (state.morse_reading)
558 int j;
559 logf("Morse: 0x%02x", state.morse_code);
560 state.morse_reading = false;
562 for (j = 0; morse_alphabets[j] != '\0'; j++)
564 if (morse_codes[j] == state.morse_code)
565 break ;
568 if (morse_alphabets[j] == '\0')
570 logf("Morse code not found");
571 break ;
574 /* turn off hangul input */
575 state.hangul = false;
576 kbd_inschar(&state, morse_alphabets[j]);
578 #endif /* HAVE_MORSE_INPUT */
579 break;
581 default:
582 if (default_event_handler(button) == SYS_USB_CONNECTED)
584 FOR_NB_SCREENS(l)
585 screens[l].setfont(FONT_SYSFIXED);
587 break;
589 } /* end switch */
591 if (button != ACTION_NONE)
593 state.cur_blink = true;
595 if (global_settings.talk_menu) /* voice UI? */
597 if (state.changed == CHANGED_PICKER)
599 if (pm->line_edit)
601 talk_id(VOICE_EDIT, false);
603 else
604 #ifdef HAVE_MORSE_INPUT
605 /* FIXME: We should talk something like Morse mode.. */
606 if (!state.morse_mode)
607 #endif
609 ch = get_kbd_ch(pm);
610 kbd_spellchar(ch);
613 else if (state.changed == CHANGED_CURSOR)
615 int c = utf8seek(state.text, state.editpos);
616 kbd_spellchar(state.text[c]);
618 else if (state.changed == CHANGED_TEXT)
619 talk_spell(state.text, false); /* speak revised text */
621 state.changed = 0;
624 #ifdef HAVE_BUTTONBAR
625 global_settings.buttonbar = buttonbar_config;
626 #endif
628 if (ret < 0)
629 splash(HZ/2, ID2P(LANG_CANCEL));
631 #if defined(HAVE_MORSE_INPUT) && defined(KBD_TOGGLE_INPUT)
632 if (global_settings.morse_input != state.morse_mode)
634 global_settings.morse_input = state.morse_mode;
635 settings_save();
637 #endif /* HAVE_MORSE_INPUT && KBD_TOGGLE_INPUT */
639 FOR_NB_SCREENS(l)
641 screens[l].setfont(FONT_UI);
642 viewportmanager_theme_undo(l, false);
644 return ret;
647 static void kbd_calc_params(struct keyboard_parameters *pm,
648 struct screen *sc, struct edit_state *state)
650 struct font* font;
651 const unsigned char *p;
652 unsigned short ch;
653 int icon_w, sc_w, sc_h, w;
654 int i, total_lines;
656 pm->curfont = pm->default_lines ? FONT_SYSFIXED : FONT_UI;
657 font = font_get(pm->curfont);
658 pm->font_h = font->height;
660 /* check if FONT_UI fits the screen */
661 if (2*pm->font_h + 3 + BUTTONBAR_HEIGHT > sc->getheight())
663 pm->curfont = FONT_SYSFIXED;
664 font = font_get(FONT_SYSFIXED);
665 pm->font_h = font->height;
668 /* find max width of keyboard glyphs.
669 * since we're going to be adding spaces,
670 * max width is at least their width */
671 pm->font_w = font_get_width(font, ' ');
672 for (i = 0; i < pm->nchars; i++)
674 if (pm->kbd_buf[i] != '\n')
676 w = font_get_width(font, pm->kbd_buf[i]);
677 if (pm->font_w < w)
678 pm->font_w = w;
682 /* Find max width for text string */
683 pm->text_w = pm->font_w;
684 p = state->text;
685 while (*p)
687 p = utf8decode(p, &ch);
688 w = font_get_width(font, ch);
689 if (pm->text_w < w)
690 pm->text_w = w;
693 /* calculate how many characters to put in a row. */
694 icon_w = get_icon_width(sc->screen_type);
695 sc_w = sc->getwidth();
696 if (pm->font_w < sc_w / pm->max_line_len)
697 pm->font_w = sc_w / pm->max_line_len;
698 pm->max_chars = sc_w / pm->font_w;
699 pm->max_chars_text = (sc_w - icon_w * 2 - 2) / pm->text_w;
700 if (pm->max_chars_text < 3 && icon_w > pm->text_w)
701 pm->max_chars_text = sc_w / pm->text_w - 2;
704 i = 0;
705 /* Pad lines with spaces */
706 while (i < pm->nchars)
708 if (pm->kbd_buf[i] == '\n')
710 int k = pm->max_chars - i % ( pm->max_chars ) - 1;
711 int j;
713 if (k == pm->max_chars - 1)
715 pm->nchars--;
717 for (j = i; j < pm->nchars; j++)
719 pm->kbd_buf[j] = pm->kbd_buf[j + 1];
722 else
724 if (pm->nchars + k - 1 >= KBD_BUF_SIZE)
725 { /* We don't want to overflow the buffer */
726 k = KBD_BUF_SIZE - pm->nchars;
729 for (j = pm->nchars + k - 1; j > i + k; j--)
731 pm->kbd_buf[j] = pm->kbd_buf[j-k];
734 pm->nchars += k;
735 k++;
737 while (k--)
739 pm->kbd_buf[i++] = ' ';
743 else
745 i++;
748 if (pm->nchars == 0)
749 pm->kbd_buf[pm->nchars++] = ' ';
751 /* calculate pm->pages and pm->lines */
752 sc_h = sc->getheight();
753 pm->lines = (sc_h - BUTTONBAR_HEIGHT) / pm->font_h - 1;
755 if (pm->default_lines && pm->lines > pm->default_lines)
756 pm->lines = pm->default_lines;
758 pm->keyboard_margin = sc_h - BUTTONBAR_HEIGHT
759 - (pm->lines+1)*pm->font_h;
761 if (pm->keyboard_margin < 3 && pm->lines > 1)
763 pm->lines--;
764 pm->keyboard_margin += pm->font_h;
767 if (pm->keyboard_margin > DEFAULT_MARGIN)
768 pm->keyboard_margin = DEFAULT_MARGIN;
770 total_lines = (pm->nchars + pm->max_chars - 1) / pm->max_chars;
771 pm->pages = (total_lines + pm->lines - 1) / pm->lines;
772 pm->lines = (total_lines + pm->pages - 1) / pm->pages;
774 pm->main_y = pm->font_h*pm->lines + pm->keyboard_margin;
775 pm->keyboard_margin -= pm->keyboard_margin/2;
777 #ifdef HAVE_MORSE_INPUT
778 pm->old_main_y = sc_h - pm->font_h - BUTTONBAR_HEIGHT;
779 if (state->morse_mode)
781 int y = pm->main_y;
782 pm->main_y = pm->old_main_y;
783 pm->old_main_y = y;
785 #endif
788 static void kbd_draw_picker(struct keyboard_parameters *pm,
789 struct screen *sc, struct edit_state *state)
791 char outline[8];
792 #ifdef HAVE_MORSE_INPUT
793 if (state->morse_mode)
795 const int w = 6, h = 8; /* sysfixed font width, height */
796 int i, j, x, y;
797 int sc_w = sc->getwidth(), sc_h = pm->main_y - pm->keyboard_margin - 1;
799 /* Draw morse code screen with sysfont */
800 sc->setfont(FONT_SYSFIXED);
801 x = 0;
802 y = 0;
803 outline[1] = '\0';
805 /* Draw morse code table with code descriptions. */
806 for (i = 0; morse_alphabets[i] != '\0'; i++)
808 int morse_code;
810 outline[0] = morse_alphabets[i];
811 sc->putsxy(x, y, outline);
813 morse_code = morse_codes[i];
814 for (j = 0; morse_code > 0x01; morse_code >>= 1)
815 j++;
817 x += w + 3 + j*4;
818 morse_code = morse_codes[i];
819 for (; morse_code > 0x01; morse_code >>= 1)
821 x -= 4;
822 if (morse_code & 0x01)
823 sc->fillrect(x, y + 2, 3, 4);
824 else
825 sc->fillrect(x, y + 3, 1, 2);
828 x += w*5 - 3;
829 if (x + w*6 >= sc_w)
831 x = 0;
832 y += h;
833 if (y + h >= sc_h)
834 break;
838 else
839 #else
840 (void) state;
841 #endif /* HAVE_MORSE_INPUT */
843 /* draw page */
844 int i, j, k;
846 sc->setfont(pm->curfont);
848 k = pm->page*pm->max_chars*pm->lines;
850 for (i = j = 0; k < pm->nchars; k++)
852 int w;
853 unsigned char *utf8;
854 utf8 = utf8encode(pm->kbd_buf[k], outline);
855 *utf8 = 0;
857 sc->getstringsize(outline, &w, NULL);
858 sc->putsxy(i*pm->font_w + (pm->font_w-w) / 2,
859 j*pm->font_h, outline);
861 if (++i >= pm->max_chars)
863 i = 0;
864 if (++j >= pm->lines)
865 break;
869 if (!pm->line_edit)
871 /* highlight the key that has focus */
872 sc->set_drawmode(DRMODE_COMPLEMENT);
873 sc->fillrect(pm->font_w*pm->x, pm->font_h*pm->y,
874 pm->font_w, pm->font_h);
875 sc->set_drawmode(DRMODE_SOLID);
880 static void kbd_draw_edit_line(struct keyboard_parameters *pm,
881 struct screen *sc, struct edit_state *state)
883 char outline[8];
884 unsigned char *utf8;
885 int i = 0, j = 0, icon_w, w;
886 int sc_w = sc->getwidth();
887 int y = pm->main_y - pm->keyboard_margin;
888 int text_margin = (sc_w - pm->text_w * pm->max_chars_text) / 2;
890 /* Clear text area one pixel above separator line so any overdraw
891 doesn't collide */
892 screen_clear_area(sc, 0, y - 1, sc_w, pm->font_h + 6);
894 sc->hline(0, sc_w - 1, y);
896 /* write out the text */
897 sc->setfont(pm->curfont);
899 pm->leftpos = MAX(0, MIN(state->len_utf8, state->editpos + 2)
900 - pm->max_chars_text);
901 pm->curpos = state->editpos - pm->leftpos;
902 utf8 = state->text + utf8seek(state->text, pm->leftpos);
904 while (*utf8 && i < pm->max_chars_text)
906 j = utf8seek(utf8, 1);
907 strlcpy(outline, utf8, j+1);
908 sc->getstringsize(outline, &w, NULL);
909 sc->putsxy(text_margin + i*pm->text_w + (pm->text_w-w)/2,
910 pm->main_y, outline);
911 utf8 += j;
912 i++;
915 icon_w = get_icon_width(sc->screen_type);
916 if (pm->leftpos > 0)
918 /* Draw nicer bitmap arrow if room, else settle for "<". */
919 if (text_margin >= icon_w)
921 screen_put_icon_with_offset(sc, 0, 0,
922 (text_margin - icon_w) / 2,
923 pm->main_y, Icon_Reverse_Cursor);
925 else
927 sc->getstringsize("<", &w, NULL);
928 sc->putsxy(text_margin - w, pm->main_y, "<");
932 if (state->len_utf8 - pm->leftpos > pm->max_chars_text)
934 /* Draw nicer bitmap arrow if room, else settle for ">". */
935 if (text_margin >= icon_w)
937 screen_put_icon_with_offset(sc, 0, 0,
938 sc_w - (text_margin + icon_w) / 2,
939 pm->main_y, Icon_Cursor);
941 else
943 sc->putsxy(sc_w - text_margin, pm->main_y, ">");
947 /* cursor */
948 i = text_margin + pm->curpos * pm->text_w;
950 if (state->cur_blink)
951 sc->vline(i, pm->main_y, pm->main_y + pm->font_h - 1);
953 if (state->hangul) /* draw underbar */
954 sc->hline(i - pm->text_w, i, pm->main_y + pm->font_h - 1);
956 if (pm->line_edit)
958 sc->set_drawmode(DRMODE_COMPLEMENT);
959 sc->fillrect(0, y + 2, sc_w, pm->font_h + 2);
960 sc->set_drawmode(DRMODE_SOLID);
964 /* inserts the selected char */
965 static void kbd_insert_selected(struct keyboard_parameters *pm,
966 struct edit_state *state)
968 /* find input char */
969 unsigned short ch = get_kbd_ch(pm);
971 /* check for hangul input */
972 if (ch >= 0x3131 && ch <= 0x3163)
974 unsigned short tmp;
976 if (!state->hangul)
978 state->hlead = state->hvowel = state->htail = 0;
979 state->hangul = true;
982 if (!state->hvowel)
984 state->hvowel = ch;
986 else if (!state->htail)
988 state->htail = ch;
990 else
992 /* previous hangul complete */
993 /* check whether tail is actually lead of next char */
994 tmp = hangul_join(state->htail, ch, 0);
996 if (tmp != 0xfffd)
998 tmp = hangul_join(state->hlead, state->hvowel, 0);
999 kbd_delchar(state);
1000 kbd_inschar(state, tmp);
1001 /* insert dummy char */
1002 kbd_inschar(state, ' ');
1003 state->hlead = state->htail;
1004 state->hvowel = ch;
1005 state->htail = 0;
1007 else
1009 state->hvowel = state->htail = 0;
1010 state->hlead = ch;
1014 /* combine into hangul */
1015 tmp = hangul_join(state->hlead, state->hvowel, state->htail);
1017 if (tmp != 0xfffd)
1019 kbd_delchar(state);
1020 ch = tmp;
1022 else
1024 state->hvowel = state->htail = 0;
1025 state->hlead = ch;
1028 else
1030 state->hangul = false;
1033 /* insert char */
1034 kbd_inschar(state, ch);
1037 static void kbd_backspace(struct edit_state *state)
1039 unsigned short ch;
1040 if (state->hangul)
1042 if (state->htail)
1043 state->htail = 0;
1044 else if (state->hvowel)
1045 state->hvowel = 0;
1046 else
1047 state->hangul = false;
1050 kbd_delchar(state);
1052 if (state->hangul)
1054 if (state->hvowel)
1055 ch = hangul_join(state->hlead, state->hvowel, state->htail);
1056 else
1057 ch = state->hlead;
1058 kbd_inschar(state, ch);
1062 static void kbd_move_cursor(struct edit_state *state, int dir)
1064 state->hangul = false;
1065 state->editpos += dir;
1067 if (state->editpos >= 0 && state->editpos <= state->len_utf8)
1069 state->changed = CHANGED_CURSOR;
1071 else
1073 state->editpos -= dir;
1074 #if CONFIG_CODEC == SWCODEC
1075 if (global_settings.talk_menu)
1076 pcmbuf_beep(1000, 150, 1500);
1077 #endif
1081 static void kbd_move_picker_horizontal(struct keyboard_parameters *pm,
1082 struct edit_state *state, int dir)
1084 state->changed = CHANGED_PICKER;
1086 pm->x += dir;
1087 if (pm->x < 0)
1089 if (--pm->page < 0)
1090 pm->page = pm->pages - 1;
1091 pm->x = pm->max_chars - 1;
1093 else if (pm->x >= pm->max_chars)
1095 if (++pm->page >= pm->pages)
1096 pm->page = 0;
1097 pm->x = 0;
1101 static void kbd_move_picker_vertical(struct keyboard_parameters *pm,
1102 struct edit_state *state, int dir)
1104 state->changed = CHANGED_PICKER;
1106 #ifdef HAVE_MORSE_INPUT
1107 if (state->morse_mode)
1109 pm->line_edit = !pm->line_edit;
1110 return;
1112 #endif /* HAVE_MORSE_INPUT */
1114 pm->y += dir;
1115 if (pm->line_edit)
1117 pm->y = (dir > 0 ? 0 : pm->lines - 1);
1118 pm->line_edit = false;
1120 else if (pm->y < 0 || pm->y >= pm->lines)
1122 pm->line_edit = true;