Move c/h files implementing/defining standard library stuff into a new libc directory...
[kugel-rb.git] / apps / recorder / keyboard.c
blobf8b390d932d181017ac1a1f9275c816d3b0716e2
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 == 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 #define CHANGED_PICKER 1
96 #define CHANGED_CURSOR 2
97 #define CHANGED_TEXT 3
99 struct keyboard_parameters
101 unsigned short kbd_buf[KBD_BUF_SIZE];
102 unsigned short max_line_len;
103 int default_lines;
104 int nchars;
105 int font_w;
106 int font_h;
107 int text_w;
108 int curfont;
109 int main_y;
110 #ifdef HAVE_MORSE_INPUT
111 int old_main_y;
112 #endif
113 int max_chars;
114 int max_chars_text;
115 int lines;
116 int pages;
117 int keyboard_margin;
118 int curpos;
119 int leftpos;
120 int page;
121 int x;
122 int y;
123 #ifdef KBD_MODES
124 bool line_edit;
125 #endif
128 struct edit_state
130 char* text;
131 int buflen;
132 int len_utf8;
133 int editpos; /* Edit position on all screens */
134 bool cur_blink; /* Cursor on/off flag */
135 bool hangul;
136 unsigned short hlead, hvowel, htail;
137 #ifdef HAVE_MORSE_INPUT
138 bool morse_mode;
139 bool morse_reading;
140 unsigned char morse_code;
141 int morse_tick;
142 #endif
143 int changed;
146 static struct keyboard_parameters kbd_param[NB_SCREENS];
147 static bool kbd_loaded = false;
149 #ifdef HAVE_MORSE_INPUT
150 /* FIXME: We should put this to a configuration file. */
151 static const char *morse_alphabets =
152 "abcdefghijklmnopqrstuvwxyz1234567890,.?-@ ";
153 static const unsigned char morse_codes[] = {
154 0x05,0x18,0x1a,0x0c,0x02,0x12,0x0e,0x10,0x04,0x17,0x0d,0x14,0x07,
155 0x06,0x0f,0x16,0x1d,0x0a,0x08,0x03,0x09,0x11,0x0b,0x19,0x1b,0x1c,
156 0x2f,0x27,0x23,0x21,0x20,0x30,0x38,0x3c,0x3e,0x3f,
157 0x73,0x55,0x4c,0x61,0x5a,0x80 };
158 #endif
160 /* Loads a custom keyboard into memory
161 call with NULL to reset keyboard */
162 int load_kbd(unsigned char* filename)
164 int fd, l;
165 int i, line_len, max_line_len;
166 unsigned char buf[4];
168 if (filename == NULL)
170 kbd_loaded = false;
171 return 0;
174 fd = open_utf8(filename, O_RDONLY|O_BINARY);
175 if (fd < 0)
176 return 1;
178 line_len = 0;
179 max_line_len = 1;
180 i = 0;
181 while (read(fd, buf, 1) == 1 && i < KBD_BUF_SIZE)
183 /* check how many bytes to read for this character */
184 static const unsigned char sizes[4] = { 0x80, 0xe0, 0xf0, 0xf5 };
185 size_t count;
186 unsigned short ch;
188 for (count = 0; count < ARRAYLEN(sizes); count++)
190 if (buf[0] < sizes[count])
191 break;
194 if (count >= ARRAYLEN(sizes))
195 continue; /* Invalid size. */
197 if (read(fd, &buf[1], count) != (ssize_t)count)
199 close(fd);
200 kbd_loaded = false;
201 return 1;
204 utf8decode(buf, &ch);
205 if (ch != 0xFEFF && ch != '\r') /* skip BOM & carriage returns */
207 FOR_NB_SCREENS(l)
208 kbd_param[l].kbd_buf[i] = ch;
209 i++;
210 if (ch == '\n')
212 if (max_line_len < line_len)
213 max_line_len = line_len;
214 line_len = 0;
216 else
217 line_len++;
221 close(fd);
222 kbd_loaded = true;
224 if (max_line_len < line_len)
225 max_line_len = line_len;
227 FOR_NB_SCREENS(l)
229 struct keyboard_parameters *pm = &kbd_param[l];
230 pm->nchars = i;
231 /* initialize parameters */
232 pm->x = pm->y = pm->page = 0;
233 pm->default_lines = 0;
234 pm->max_line_len = max_line_len;
237 return 0;
240 /* helper function to spell a char */
241 static void kbd_spellchar(unsigned short c)
243 unsigned char tmp[5];
244 /* store char to pass to talk_spell */
245 unsigned char* utf8 = utf8encode(c, tmp);
246 *utf8 = 0;
248 if (c == ' ')
249 talk_id(VOICE_BLANK, false);
250 else
251 talk_spell(tmp, false);
254 static void kbd_inschar(struct edit_state *state, unsigned short ch)
256 int i, j, len;
257 unsigned char tmp[4];
258 unsigned char* utf8;
260 len = strlen(state->text);
261 utf8 = utf8encode(ch, tmp);
262 j = (long)utf8 - (long)tmp;
264 if (len + j < state->buflen)
266 i = utf8seek(state->text, state->editpos);
267 utf8 = state->text + i;
268 memmove(utf8 + j, utf8, len - i + 1);
269 memcpy(utf8, tmp, j);
270 state->editpos++;
271 state->changed = CHANGED_TEXT;
275 static void kbd_delchar(struct edit_state *state)
277 int i, j, len;
278 unsigned char* utf8;
280 if (state->editpos > 0)
282 state->editpos--;
283 len = strlen(state->text);
284 i = utf8seek(state->text, state->editpos);
285 utf8 = state->text + i;
286 j = utf8seek(utf8, 1);
287 memmove(utf8, utf8 + j, len - i - j + 1);
288 state->changed = CHANGED_TEXT;
292 /* Lookup k value based on state of param (pm) */
293 static unsigned short get_kbd_ch(const struct keyboard_parameters *pm)
295 int k = (pm->page*pm->lines + pm->y)*pm->max_chars + pm->x;
296 return (k < pm->nchars)? pm->kbd_buf[k]: ' ';
299 static void kbd_calc_params(struct keyboard_parameters *pm,
300 struct screen *sc, struct edit_state *state);
301 static void kbd_draw_picker(struct keyboard_parameters *pm,
302 struct screen *sc, struct edit_state *state);
303 static void kbd_draw_edit_line(struct keyboard_parameters *pm,
304 struct screen *sc, struct edit_state *state);
305 static void kbd_insert_selected(struct keyboard_parameters *pm,
306 struct edit_state *state);
307 static void kbd_backspace(struct edit_state *state);
308 static void kbd_move_cursor(struct edit_state *state, int dir);
309 static void kbd_move_picker_vertical(struct keyboard_parameters *pm,
310 struct edit_state *state, int dir);
312 int kbd_input(char* text, int buflen)
314 bool done = false;
315 #ifdef CPU_ARM
316 /* This seems to keep the sizes for ARM way down */
317 struct keyboard_parameters * volatile param = kbd_param;
318 #else
319 struct keyboard_parameters * const param = kbd_param;
320 #endif
321 struct edit_state state;
322 int l; /* screen loop variable */
323 unsigned short ch;
324 int ret = 0; /* assume success */
325 FOR_NB_SCREENS(l)
327 viewportmanager_theme_enable(l, false, NULL);
330 #ifdef HAVE_BUTTONBAR
331 struct gui_buttonbar buttonbar;
332 bool buttonbar_config = global_settings.buttonbar;
334 global_settings.buttonbar = true;
335 gui_buttonbar_init(&buttonbar);
336 gui_buttonbar_set_display(&buttonbar, &screens[SCREEN_MAIN]);
337 #endif
339 /* initialize state */
340 state.text = text;
341 state.buflen = buflen;
342 /* Initial edit position is after last character */
343 state.editpos = utf8length(state.text);
344 state.cur_blink = true;
345 #ifdef HAVE_MORSE_INPUT
346 state.morse_mode = global_settings.morse_input;
347 state.morse_reading = false;
348 #endif
349 state.hangul = false;
350 state.changed = 0;
352 if (!kbd_loaded)
354 /* Copy default keyboard to buffer */
355 FOR_NB_SCREENS(l)
357 struct keyboard_parameters *pm = &param[l];
358 const unsigned char *p;
359 int i = 0;
361 #if LCD_WIDTH >= 160 && LCD_HEIGHT >= 96
362 struct screen *sc = &screens[l];
364 if (sc->getwidth() >= 160 && sc->getheight() >= 96)
366 p = "ABCDEFG abcdefg !?\" @#$%+'\n"
367 "HIJKLMN hijklmn 789 &_()-`\n"
368 "OPQRSTU opqrstu 456 §|{}/<\n"
369 "VWXYZ., vwxyz.,0123 ~=[]*>\n"
370 "ÀÁÂÃÄÅÆ ÌÍÎÏ ÈÉÊË ¢£¤¥¦§©®\n"
371 "àáâãäåæ ìíîï èéêë «»°ºª¹²³\n"
372 "ÓÒÔÕÖØ ÇÐÞÝß ÙÚÛÜ ¯±×÷¡¿µ·\n"
373 "òóôõöø çðþýÿ ùúûü ¼½¾¬¶¨:;";
375 pm->default_lines = 8;
376 pm->max_line_len = 26;
378 else
379 #endif /* LCD_WIDTH >= 160 && LCD_HEIGHT >= 96 */
381 p = "ABCDEFG !?\" @#$%+'\n"
382 "HIJKLMN 789 &_()-`\n"
383 "OPQRSTU 456 §|{}/<\n"
384 "VWXYZ.,0123 ~=[]*>\n"
386 "abcdefg ¢£¤¥¦§©®¬\n"
387 "hijklmn «»°ºª¹²³¶\n"
388 "opqrstu ¯±×÷¡¿µ·¨\n"
389 "vwxyz., :;¼½¾ \n"
391 "ÀÁÂÃÄÅÆ ÌÍÎÏ ÈÉÊË\n"
392 "àáâãäåæ ìíîï èéêë\n"
393 "ÓÒÔÕÖØ ÇÐÞÝß ÙÚÛÜ\n"
394 "òóôõöø çðþýÿ ùúûü";
396 pm->default_lines = 4;
397 pm->max_line_len = 18;
400 while (*p)
401 p = utf8decode(p, &pm->kbd_buf[i++]);
403 pm->nchars = i;
404 /* initialize parameters */
405 pm->x = pm->y = pm->page = 0;
407 kbd_loaded = true;
410 FOR_NB_SCREENS(l)
412 struct keyboard_parameters *pm = &param[l];
413 struct screen *sc = &screens[l];
414 kbd_calc_params(pm, sc, &state);
417 if (global_settings.talk_menu) /* voice UI? */
418 talk_spell(state.text, true); /* spell initial text */
420 while (!done)
422 /* These declarations are assigned to the screen on which the key
423 action occurred - pointers save a lot of space over array notation
424 when accessing the same array element countless times */
425 int button;
426 #if NB_SCREENS > 1
427 int button_screen;
428 #else
429 const int button_screen = 0;
430 #endif
431 struct keyboard_parameters *pm;
432 struct screen *sc;
434 state.len_utf8 = utf8length(state.text);
436 FOR_NB_SCREENS(l)
438 /* declare scoped pointers inside screen loops - hide the
439 declarations from previous block level */
440 struct keyboard_parameters *pm = &param[l];
441 struct screen *sc = &screens[l];
442 sc->clear_display();
443 kbd_draw_picker(pm, sc, &state);
444 kbd_draw_edit_line(pm, sc, &state);
447 #ifdef HAVE_BUTTONBAR
448 /* draw the button bar */
449 gui_buttonbar_set(&buttonbar, "Shift", "OK", "Del");
450 gui_buttonbar_draw(&buttonbar);
451 #endif
453 FOR_NB_SCREENS(l)
454 screens[l].update();
456 state.cur_blink = !state.cur_blink;
458 button = get_action(
459 #ifdef HAVE_MORSE_INPUT
460 state.morse_mode? CONTEXT_MORSE_INPUT:
461 #endif
462 CONTEXT_KEYBOARD, HZ/2);
463 #if NB_SCREENS > 1
464 button_screen = (get_action_statuscode(NULL) & ACTION_REMOTE) ? 1 : 0;
465 #endif
466 pm = &param[button_screen];
467 sc = &screens[button_screen];
469 #if defined(KBD_MODES) || defined(HAVE_MORSE_INPUT)
470 /* Remap some buttons to allow to move
471 * cursor in line edit mode and morse mode. */
472 #if defined(KBD_MODES) && defined(HAVE_MORSE_INPUT)
473 if (pm->line_edit || state.morse_mode)
474 #elif defined(KBD_MODES)
475 if (pm->line_edit)
476 #else /* defined(HAVE_MORSE_INPUT) */
477 if (state.morse_mode)
478 #endif
480 if (button == ACTION_KBD_LEFT)
481 button = ACTION_KBD_CURSOR_LEFT;
482 if (button == ACTION_KBD_RIGHT)
483 button = ACTION_KBD_CURSOR_RIGHT;
484 #ifdef KBD_MODES
485 /* select doubles as backspace in line_edit */
486 if (pm->line_edit && button == ACTION_KBD_SELECT)
487 button = ACTION_KBD_BACKSPACE;
488 #endif
490 #endif /* defined(KBD_MODES) || defined(HAVE_MORSE_INPUT) */
492 switch ( button )
494 case ACTION_KBD_DONE:
495 /* accepts what was entered and continues */
496 ret = 0;
497 done = true;
498 break;
500 case ACTION_KBD_ABORT:
501 ret = -1;
502 done = true;
503 break;
505 case ACTION_KBD_PAGE_FLIP:
506 #ifdef HAVE_MORSE_INPUT
507 if (state.morse_mode)
508 break;
509 #endif
510 if (++pm->page >= pm->pages)
511 pm->page = 0;
513 state.changed = CHANGED_PICKER;
514 break;
516 case ACTION_KBD_RIGHT:
517 if (++pm->x >= pm->max_chars)
519 #ifndef KBD_PAGE_FLIP
520 /* no dedicated flip key - flip page on wrap */
521 if (++pm->page >= pm->pages)
522 pm->page = 0;
523 #endif
524 pm->x = 0;
527 state.changed = CHANGED_PICKER;
528 break;
530 case ACTION_KBD_LEFT:
531 if (--pm->x < 0)
533 #ifndef KBD_PAGE_FLIP
534 /* no dedicated flip key - flip page on wrap */
535 if (--pm->page < 0)
536 pm->page = pm->pages - 1;
537 #endif
538 pm->x = pm->max_chars - 1;
541 state.changed = CHANGED_PICKER;
542 break;
544 case ACTION_KBD_DOWN:
545 kbd_move_picker_vertical(pm, &state, 1);
546 break;
548 case ACTION_KBD_UP:
549 kbd_move_picker_vertical(pm, &state, -1);
550 break;
552 #ifdef HAVE_MORSE_INPUT
553 #ifdef KBD_TOGGLE_INPUT
554 case ACTION_KBD_MORSE_INPUT:
555 state.morse_mode = !state.morse_mode;
557 FOR_NB_SCREENS(l)
559 struct keyboard_parameters *pm = &param[l];
560 int y = pm->main_y;
561 pm->main_y = pm->old_main_y;
562 pm->old_main_y = y;
564 /* FIXME: We should talk something like Morse mode.. */
565 break;
566 #endif /* KBD_TOGGLE_INPUT */
568 case ACTION_KBD_MORSE_SELECT:
569 if (state.morse_mode && state.morse_reading)
571 state.morse_code <<= 1;
572 if ((current_tick - state.morse_tick) > HZ/5)
573 state.morse_code |= 0x01;
575 break;
576 #endif /* HAVE_MORSE_INPUT */
578 case ACTION_KBD_SELECT:
579 #ifdef HAVE_MORSE_INPUT
580 if (state.morse_mode)
582 state.morse_tick = current_tick;
584 if (!state.morse_reading)
586 state.morse_reading = true;
587 state.morse_code = 1;
590 else
591 #endif /* HAVE_MORSE_INPUT */
592 kbd_insert_selected(pm, &state);
593 break;
595 case ACTION_KBD_BACKSPACE:
596 kbd_backspace(&state);
597 break;
599 case ACTION_KBD_CURSOR_RIGHT:
600 kbd_move_cursor(&state, 1);
601 break;
603 case ACTION_KBD_CURSOR_LEFT:
604 kbd_move_cursor(&state, -1);
605 break;
607 case ACTION_NONE:
608 #ifdef HAVE_MORSE_INPUT
609 if (state.morse_reading)
611 int j;
612 logf("Morse: 0x%02x", state.morse_code);
613 state.morse_reading = false;
615 for (j = 0; morse_alphabets[j] != '\0'; j++)
617 if (morse_codes[j] == state.morse_code)
618 break ;
621 if (morse_alphabets[j] == '\0')
623 logf("Morse code not found");
624 break ;
627 /* turn off hangul input */
628 state.hangul = false;
629 kbd_inschar(&state, morse_alphabets[j]);
631 #endif /* HAVE_MORSE_INPUT */
632 break;
634 default:
635 if (default_event_handler(button) == SYS_USB_CONNECTED)
637 FOR_NB_SCREENS(l)
638 screens[l].setfont(FONT_SYSFIXED);
640 break;
642 } /* end switch */
644 if (button != ACTION_NONE)
646 state.cur_blink = true;
648 if (global_settings.talk_menu) /* voice UI? */
650 if (state.changed == CHANGED_PICKER)
652 #ifdef KBD_MODES
653 if (pm->line_edit)
655 talk_id(VOICE_EDIT, false);
657 else
658 #endif
659 #ifdef HAVE_MORSE_INPUT
660 if (!state.morse_mode)
661 #endif
663 ch = get_kbd_ch(pm);
664 kbd_spellchar(ch);
667 else if (state.changed == CHANGED_CURSOR)
669 int c = utf8seek(state.text, state.editpos);
670 kbd_spellchar(state.text[c]);
672 else if (state.changed == CHANGED_TEXT)
673 talk_spell(state.text, false); /* speak revised text */
675 state.changed = 0;
678 #ifdef HAVE_BUTTONBAR
679 global_settings.buttonbar = buttonbar_config;
680 #endif
682 if (ret < 0)
683 splash(HZ/2, ID2P(LANG_CANCEL));
685 #if defined(HAVE_MORSE_INPUT) && defined(KBD_TOGGLE_INPUT)
686 if (global_settings.morse_input != state.morse_mode)
688 global_settings.morse_input = state.morse_mode;
689 settings_save();
691 #endif /* HAVE_MORSE_INPUT && KBD_TOGGLE_INPUT */
693 FOR_NB_SCREENS(l)
695 screens[l].setfont(FONT_UI);
696 viewportmanager_theme_undo(l, false);
698 return ret;
701 static void kbd_calc_params(struct keyboard_parameters *pm,
702 struct screen *sc, struct edit_state *state)
704 struct font* font;
705 const unsigned char *p;
706 unsigned short ch;
707 int icon_w, sc_w, sc_h, w;
708 int i, total_lines;
710 pm->curfont = pm->default_lines ? FONT_SYSFIXED : FONT_UI;
711 font = font_get(pm->curfont);
712 pm->font_h = font->height;
714 /* check if FONT_UI fits the screen */
715 if (2*pm->font_h + 3 + BUTTONBAR_HEIGHT > sc->getheight())
717 pm->curfont = FONT_SYSFIXED;
718 font = font_get(FONT_SYSFIXED);
719 pm->font_h = font->height;
722 /* find max width of keyboard glyphs.
723 * since we're going to be adding spaces,
724 * max width is at least their width */
725 pm->font_w = font_get_width(font, ' ');
726 for (i = 0; i < pm->nchars; i++)
728 if (pm->kbd_buf[i] != '\n')
730 w = font_get_width(font, pm->kbd_buf[i]);
731 if (pm->font_w < w)
732 pm->font_w = w;
736 /* Find max width for text string */
737 pm->text_w = pm->font_w;
738 p = state->text;
739 while (*p)
741 p = utf8decode(p, &ch);
742 w = font_get_width(font, ch);
743 if (pm->text_w < w)
744 pm->text_w = w;
747 /* calculate how many characters to put in a row. */
748 icon_w = get_icon_width(sc->screen_type);
749 sc_w = sc->getwidth();
750 if (pm->font_w < sc_w / pm->max_line_len)
751 pm->font_w = sc_w / pm->max_line_len;
752 pm->max_chars = sc_w / pm->font_w;
753 pm->max_chars_text = (sc_w - icon_w * 2 - 2) / pm->text_w;
754 if (pm->max_chars_text < 3 && icon_w > pm->text_w)
755 pm->max_chars_text = sc_w / pm->text_w - 2;
758 i = 0;
759 /* Pad lines with spaces */
760 while (i < pm->nchars)
762 if (pm->kbd_buf[i] == '\n')
764 int k = pm->max_chars - i % ( pm->max_chars ) - 1;
765 int j;
767 if (k == pm->max_chars - 1)
769 pm->nchars--;
771 for (j = i; j < pm->nchars; j++)
773 pm->kbd_buf[j] = pm->kbd_buf[j + 1];
776 else
778 if (pm->nchars + k - 1 >= KBD_BUF_SIZE)
779 { /* We don't want to overflow the buffer */
780 k = KBD_BUF_SIZE - pm->nchars;
783 for (j = pm->nchars + k - 1; j > i + k; j--)
785 pm->kbd_buf[j] = pm->kbd_buf[j-k];
788 pm->nchars += k;
789 k++;
791 while (k--)
793 pm->kbd_buf[i++] = ' ';
797 else
799 i++;
802 if (pm->nchars == 0)
803 pm->kbd_buf[pm->nchars++] = ' ';
805 /* calculate pm->pages and pm->lines */
806 sc_h = sc->getheight();
807 pm->lines = (sc_h - BUTTONBAR_HEIGHT) / pm->font_h - 1;
809 if (pm->default_lines && pm->lines > pm->default_lines)
810 pm->lines = pm->default_lines;
812 pm->keyboard_margin = sc_h - BUTTONBAR_HEIGHT
813 - (pm->lines+1)*pm->font_h;
815 if (pm->keyboard_margin < 3 && pm->lines > 1)
817 pm->lines--;
818 pm->keyboard_margin += pm->font_h;
821 if (pm->keyboard_margin > DEFAULT_MARGIN)
822 pm->keyboard_margin = DEFAULT_MARGIN;
824 total_lines = (pm->nchars + pm->max_chars - 1) / pm->max_chars;
825 pm->pages = (total_lines + pm->lines - 1) / pm->lines;
826 pm->lines = (total_lines + pm->pages - 1) / pm->pages;
828 pm->main_y = pm->font_h*pm->lines + pm->keyboard_margin;
829 pm->keyboard_margin -= pm->keyboard_margin/2;
831 #ifdef HAVE_MORSE_INPUT
832 pm->old_main_y = sc_h - pm->font_h - BUTTONBAR_HEIGHT;
833 if (state->morse_mode)
835 int y = pm->main_y;
836 pm->main_y = pm->old_main_y;
837 pm->old_main_y = y;
839 #endif
842 static void kbd_draw_picker(struct keyboard_parameters *pm,
843 struct screen *sc, struct edit_state *state)
845 char outline[8];
846 #ifdef HAVE_MORSE_INPUT
847 if (state->morse_mode)
849 const int w = 6, h = 8; /* sysfixed font width, height */
850 int i, j, x, y;
851 int sc_w = sc->getwidth(), sc_h = pm->main_y - pm->keyboard_margin - 1;
853 /* Draw morse code screen with sysfont */
854 sc->setfont(FONT_SYSFIXED);
855 x = 0;
856 y = 0;
857 outline[1] = '\0';
859 /* Draw morse code table with code descriptions. */
860 for (i = 0; morse_alphabets[i] != '\0'; i++)
862 int morse_code;
864 outline[0] = morse_alphabets[i];
865 sc->putsxy(x, y, outline);
867 morse_code = morse_codes[i];
868 for (j = 0; morse_code > 0x01; morse_code >>= 1)
869 j++;
871 x += w + 3 + j*4;
872 morse_code = morse_codes[i];
873 for (; morse_code > 0x01; morse_code >>= 1)
875 x -= 4;
876 if (morse_code & 0x01)
877 sc->fillrect(x, y + 2, 3, 4);
878 else
879 sc->fillrect(x, y + 3, 1, 2);
882 x += w*5 - 3;
883 if (x + w*6 >= sc_w)
885 x = 0;
886 y += h;
887 if (y + h >= sc_h)
888 break;
892 else
893 #else
894 (void) state;
895 #endif /* HAVE_MORSE_INPUT */
897 /* draw page */
898 int i, j, k;
900 sc->setfont(pm->curfont);
902 k = pm->page*pm->max_chars*pm->lines;
904 for (i = j = 0; k < pm->nchars; k++)
906 int w;
907 unsigned char *utf8;
908 utf8 = utf8encode(pm->kbd_buf[k], outline);
909 *utf8 = 0;
911 sc->getstringsize(outline, &w, NULL);
912 sc->putsxy(i*pm->font_w + (pm->font_w-w) / 2,
913 j*pm->font_h, outline);
915 if (++i >= pm->max_chars)
917 i = 0;
918 if (++j >= pm->lines)
919 break;
923 #ifdef KBD_MODES
924 if (!pm->line_edit)
925 #endif
927 /* highlight the key that has focus */
928 sc->set_drawmode(DRMODE_COMPLEMENT);
929 sc->fillrect(pm->font_w*pm->x, pm->font_h*pm->y,
930 pm->font_w, pm->font_h);
931 sc->set_drawmode(DRMODE_SOLID);
936 static void kbd_draw_edit_line(struct keyboard_parameters *pm,
937 struct screen *sc, struct edit_state *state)
939 char outline[8];
940 unsigned char *utf8;
941 int i = 0, j = 0, icon_w, w;
942 int sc_w = sc->getwidth();
943 int y = pm->main_y - pm->keyboard_margin;
944 int text_margin = (sc_w - pm->text_w * pm->max_chars_text) / 2;
946 /* Clear text area one pixel above separator line so any overdraw
947 doesn't collide */
948 screen_clear_area(sc, 0, y - 1, sc_w, pm->font_h + 6);
950 sc->hline(0, sc_w - 1, y);
952 /* write out the text */
953 sc->setfont(pm->curfont);
955 pm->leftpos = MAX(0, MIN(state->len_utf8, state->editpos + 2)
956 - pm->max_chars_text);
957 pm->curpos = state->editpos - pm->leftpos;
958 utf8 = state->text + utf8seek(state->text, pm->leftpos);
960 while (*utf8 && i < pm->max_chars_text)
962 j = utf8seek(utf8, 1);
963 strlcpy(outline, utf8, j+1);
964 sc->getstringsize(outline, &w, NULL);
965 sc->putsxy(text_margin + i*pm->text_w + (pm->text_w-w)/2,
966 pm->main_y, outline);
967 utf8 += j;
968 i++;
971 icon_w = get_icon_width(sc->screen_type);
972 if (pm->leftpos > 0)
974 /* Draw nicer bitmap arrow if room, else settle for "<". */
975 if (text_margin >= icon_w)
977 screen_put_icon_with_offset(sc, 0, 0,
978 (text_margin - icon_w) / 2,
979 pm->main_y, Icon_Reverse_Cursor);
981 else
983 sc->getstringsize("<", &w, NULL);
984 sc->putsxy(text_margin - w, pm->main_y, "<");
988 if (state->len_utf8 - pm->leftpos > pm->max_chars_text)
990 /* Draw nicer bitmap arrow if room, else settle for ">". */
991 if (text_margin >= icon_w)
993 screen_put_icon_with_offset(sc, 0, 0,
994 sc_w - (text_margin + icon_w) / 2,
995 pm->main_y, Icon_Cursor);
997 else
999 sc->putsxy(sc_w - text_margin, pm->main_y, ">");
1003 /* cursor */
1004 i = text_margin + pm->curpos * pm->text_w;
1006 if (state->cur_blink)
1007 sc->vline(i, pm->main_y, pm->main_y + pm->font_h - 1);
1009 if (state->hangul) /* draw underbar */
1010 sc->hline(i - pm->text_w, i, pm->main_y + pm->font_h - 1);
1012 #ifdef KBD_MODES
1013 if (pm->line_edit)
1015 sc->set_drawmode(DRMODE_COMPLEMENT);
1016 sc->fillrect(0, y + 2, sc_w, pm->font_h + 2);
1017 sc->set_drawmode(DRMODE_SOLID);
1019 #endif
1022 /* inserts the selected char */
1023 static void kbd_insert_selected(struct keyboard_parameters *pm,
1024 struct edit_state *state)
1026 /* find input char */
1027 unsigned short ch = get_kbd_ch(pm);
1029 /* check for hangul input */
1030 if (ch >= 0x3131 && ch <= 0x3163)
1032 unsigned short tmp;
1034 if (!state->hangul)
1036 state->hlead = state->hvowel = state->htail = 0;
1037 state->hangul = true;
1040 if (!state->hvowel)
1042 state->hvowel = ch;
1044 else if (!state->htail)
1046 state->htail = ch;
1048 else
1050 /* previous hangul complete */
1051 /* check whether tail is actually lead of next char */
1052 tmp = hangul_join(state->htail, ch, 0);
1054 if (tmp != 0xfffd)
1056 tmp = hangul_join(state->hlead, state->hvowel, 0);
1057 kbd_delchar(state);
1058 kbd_inschar(state, tmp);
1059 /* insert dummy char */
1060 kbd_inschar(state, ' ');
1061 state->hlead = state->htail;
1062 state->hvowel = ch;
1063 state->htail = 0;
1065 else
1067 state->hvowel = state->htail = 0;
1068 state->hlead = ch;
1072 /* combine into hangul */
1073 tmp = hangul_join(state->hlead, state->hvowel, state->htail);
1075 if (tmp != 0xfffd)
1077 kbd_delchar(state);
1078 ch = tmp;
1080 else
1082 state->hvowel = state->htail = 0;
1083 state->hlead = ch;
1086 else
1088 state->hangul = false;
1091 /* insert char */
1092 kbd_inschar(state, ch);
1095 static void kbd_backspace(struct edit_state *state)
1097 unsigned short ch;
1098 if (state->hangul)
1100 if (state->htail)
1101 state->htail = 0;
1102 else if (state->hvowel)
1103 state->hvowel = 0;
1104 else
1105 state->hangul = false;
1108 kbd_delchar(state);
1110 if (state->hangul)
1112 if (state->hvowel)
1113 ch = hangul_join(state->hlead, state->hvowel, state->htail);
1114 else
1115 ch = state->hlead;
1116 kbd_inschar(state, ch);
1120 static void kbd_move_cursor(struct edit_state *state, int dir)
1122 state->hangul = false;
1123 state->editpos += dir;
1125 if (state->editpos >= 0 && state->editpos <= state->len_utf8)
1127 state->changed = CHANGED_CURSOR;
1129 else
1131 state->editpos -= dir;
1132 #if CONFIG_CODEC == SWCODEC
1133 if (global_settings.talk_menu)
1134 pcmbuf_beep(1000, 150, 1500);
1135 #endif
1139 static void kbd_move_picker_vertical(struct keyboard_parameters *pm,
1140 struct edit_state *state, int dir)
1142 (void) state;
1143 state->changed = CHANGED_PICKER;
1144 #ifdef HAVE_MORSE_INPUT
1145 if (state->morse_mode)
1147 #ifdef KBD_MODES
1148 pm->line_edit = !pm->line_edit;
1149 #endif
1150 return;
1152 #endif /* HAVE_MORSE_INPUT */
1153 pm->y += dir;
1154 #ifdef KBD_MODES
1155 if (pm->line_edit)
1157 pm->y = (dir > 0 ? 0 : pm->lines - 1);
1158 pm->line_edit = false;
1160 else if (pm->y < 0 || pm->y >= pm->lines)
1162 pm->line_edit = true;
1164 #else
1165 if (pm->y >= pm->lines)
1166 pm->y = 0;
1167 if (pm->y < 0)
1168 pm->y = pm->lines - 1;
1169 #endif