1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
23 #include "string-extra.h"
29 #include "rbunicode.h"
30 #include "buttonbar.h"
47 #define DEFAULT_MARGIN 6
48 #define KBD_BUF_SIZE 500
50 #ifdef HAVE_TOUCHSCREEN
51 #define MIN_GRID_SIZE 16
52 #define GRID_SIZE(s, x) \
53 ((s) == SCREEN_MAIN && MIN_GRID_SIZE > (x) ? MIN_GRID_SIZE: (x))
56 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) \
57 || (CONFIG_KEYPAD == IRIVER_H300_PAD) \
58 || (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
59 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
60 || (CONFIG_KEYPAD == IPOD_4G_PAD) \
61 || (CONFIG_KEYPAD == IRIVER_H10_PAD) \
62 || (CONFIG_KEYPAD == GIGABEAT_PAD) \
63 || (CONFIG_KEYPAD == GIGABEAT_S_PAD) \
64 || (CONFIG_KEYPAD == MROBE100_PAD) \
65 || (CONFIG_KEYPAD == SANSA_E200_PAD) \
66 || (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD) \
67 || (CONFIG_KEYPAD == PHILIPS_SA9200_PAD) \
68 || (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
69 /* certain key combos toggle input mode between keyboard input and Morse input */
70 #define KBD_TOGGLE_INPUT
73 #define CHANGED_PICKER 1
74 #define CHANGED_CURSOR 2
75 #define CHANGED_TEXT 3
77 struct keyboard_parameters
79 unsigned short kbd_buf
[KBD_BUF_SIZE
];
80 unsigned short max_line_len
;
89 #ifdef HAVE_MORSE_INPUT
103 #ifdef HAVE_TOUCHSCREEN
113 int editpos
; /* Edit position on all screens */
114 bool cur_blink
; /* Cursor on/off flag */
116 unsigned short hlead
, hvowel
, htail
;
117 #ifdef HAVE_MORSE_INPUT
120 unsigned char morse_code
;
126 static struct keyboard_parameters kbd_param
[NB_SCREENS
];
127 static bool kbd_loaded
= false;
129 #ifdef HAVE_MORSE_INPUT
130 /* FIXME: We should put this to a configuration file. */
131 static const char *morse_alphabets
=
132 "abcdefghijklmnopqrstuvwxyz1234567890,.?-@ ";
133 static const unsigned char morse_codes
[] = {
134 0x05,0x18,0x1a,0x0c,0x02,0x12,0x0e,0x10,0x04,0x17,0x0d,0x14,0x07,
135 0x06,0x0f,0x16,0x1d,0x0a,0x08,0x03,0x09,0x11,0x0b,0x19,0x1b,0x1c,
136 0x2f,0x27,0x23,0x21,0x20,0x30,0x38,0x3c,0x3e,0x3f,
137 0x73,0x55,0x4c,0x61,0x5a,0x80 };
140 /* Loads a custom keyboard into memory
141 call with NULL to reset keyboard */
142 int load_kbd(unsigned char* filename
)
145 int i
, line_len
, max_line_len
;
146 unsigned char buf
[4];
147 unsigned short *pbuf
;
149 if (filename
== NULL
)
155 fd
= open_utf8(filename
, O_RDONLY
|O_BINARY
);
159 pbuf
= kbd_param
[0].kbd_buf
;
163 while (read(fd
, buf
, 1) == 1 && i
< KBD_BUF_SIZE
-1)
165 /* check how many bytes to read for this character */
166 static const unsigned char sizes
[4] = { 0x80, 0xe0, 0xf0, 0xf5 };
170 for (count
= 0; count
< ARRAYLEN(sizes
); count
++)
172 if (buf
[0] < sizes
[count
])
176 if (count
>= ARRAYLEN(sizes
))
177 continue; /* Invalid size. */
179 if (read(fd
, &buf
[1], count
) != (ssize_t
)count
)
186 utf8decode(buf
, &ch
);
187 if (ch
!= 0xFEFF && ch
!= '\r') /* skip BOM & carriage returns */
192 if (max_line_len
< line_len
)
193 max_line_len
= line_len
;
195 pbuf
+= line_len
+ 1;
199 pbuf
[++line_len
] = ch
;
206 if (max_line_len
< line_len
)
207 max_line_len
= line_len
;
208 if (i
== 1 || line_len
!= 0) /* ignore last empty line */
211 pbuf
+= line_len
+ 1;
213 *pbuf
= 0xFEFF; /* mark end of characters */
217 struct keyboard_parameters
*pm
= &kbd_param
[l
];
220 memcpy(pm
->kbd_buf
, kbd_param
[0].kbd_buf
, i
*sizeof(unsigned short));
222 /* initialize parameters */
223 pm
->x
= pm
->y
= pm
->page
= 0;
224 pm
->default_lines
= 0;
225 pm
->max_line_len
= max_line_len
;
231 /* helper function to spell a char */
232 static void kbd_spellchar(unsigned short c
)
234 unsigned char tmp
[5];
235 /* store char to pass to talk_spell */
236 unsigned char* utf8
= utf8encode(c
, tmp
);
240 talk_id(VOICE_BLANK
, false);
242 talk_spell(tmp
, false);
245 static void kbd_inschar(struct edit_state
*state
, unsigned short ch
)
248 unsigned char tmp
[4];
251 len
= strlen(state
->text
);
252 utf8
= utf8encode(ch
, tmp
);
253 j
= (long)utf8
- (long)tmp
;
255 if (len
+ j
< state
->buflen
)
257 i
= utf8seek(state
->text
, state
->editpos
);
258 utf8
= state
->text
+ i
;
259 memmove(utf8
+ j
, utf8
, len
- i
+ 1);
260 memcpy(utf8
, tmp
, j
);
262 state
->changed
= CHANGED_TEXT
;
266 static void kbd_delchar(struct edit_state
*state
)
271 if (state
->editpos
> 0)
274 len
= strlen(state
->text
);
275 i
= utf8seek(state
->text
, state
->editpos
);
276 utf8
= state
->text
+ i
;
277 j
= utf8seek(utf8
, 1);
278 memmove(utf8
, utf8
+ j
, len
- i
- j
+ 1);
279 state
->changed
= CHANGED_TEXT
;
283 /* Lookup k value based on state of param (pm) */
284 static unsigned short get_kbd_ch(struct keyboard_parameters
*pm
, int x
, int y
)
286 int i
= 0, k
= pm
->page
*pm
->lines
+ y
, n
;
287 unsigned short *pbuf
;
293 for (pbuf
= &pm
->kbd_buf
[i
]; (i
= *pbuf
) != 0xFEFF; pbuf
+= i
+ 1)
295 n
= i
? (i
+ pm
->max_chars
- 1) / pm
->max_chars
: 1;
299 if (y
== 0 && i
!= 0xFEFF)
301 pm
->last_k
= pm
->page
*pm
->lines
- k
;
302 pm
->last_i
= pbuf
- pm
->kbd_buf
;
304 k
= k
* pm
->max_chars
+ x
;
305 return (*pbuf
!= 0xFEFF && k
< *pbuf
)? pbuf
[k
+1]: ' ';
308 static void kbd_calc_params(struct keyboard_parameters
*pm
,
309 struct screen
*sc
, struct edit_state
*state
);
310 static void kbd_draw_picker(struct keyboard_parameters
*pm
,
311 struct screen
*sc
, struct edit_state
*state
);
312 static void kbd_draw_edit_line(struct keyboard_parameters
*pm
,
313 struct screen
*sc
, struct edit_state
*state
);
314 #ifdef HAVE_TOUCHSCREEN
315 static void kbd_draw_buttons(struct keyboard_parameters
*pm
, struct screen
*sc
);
316 static int keyboard_touchscreen(struct keyboard_parameters
*pm
,
317 struct screen
*sc
, struct edit_state
*state
);
319 static void kbd_insert_selected(struct keyboard_parameters
*pm
,
320 struct edit_state
*state
);
321 static void kbd_backspace(struct edit_state
*state
);
322 static void kbd_move_cursor(struct edit_state
*state
, int dir
);
323 static void kbd_move_picker_horizontal(struct keyboard_parameters
*pm
,
324 struct edit_state
*state
, int dir
);
325 static void kbd_move_picker_vertical(struct keyboard_parameters
*pm
,
326 struct edit_state
*state
, int dir
);
328 int kbd_input(char* text
, int buflen
)
332 /* This seems to keep the sizes for ARM way down */
333 struct keyboard_parameters
* volatile param
= kbd_param
;
335 struct keyboard_parameters
* const param
= kbd_param
;
337 struct edit_state state
;
338 int l
; /* screen loop variable */
340 int ret
= 0; /* assume success */
343 viewportmanager_theme_enable(l
, false, NULL
);
346 #ifdef HAVE_BUTTONBAR
347 struct gui_buttonbar buttonbar
;
348 bool buttonbar_config
= global_settings
.buttonbar
;
350 global_settings
.buttonbar
= true;
351 gui_buttonbar_init(&buttonbar
);
352 gui_buttonbar_set_display(&buttonbar
, &screens
[SCREEN_MAIN
]);
355 /* initialize state */
357 state
.buflen
= buflen
;
358 /* Initial edit position is after last character */
359 state
.editpos
= utf8length(state
.text
);
360 state
.cur_blink
= true;
361 #ifdef HAVE_MORSE_INPUT
362 state
.morse_mode
= global_settings
.morse_input
;
363 state
.morse_reading
= false;
365 state
.hangul
= false;
370 /* Copy default keyboard to buffer */
373 struct keyboard_parameters
*pm
= ¶m
[l
];
374 unsigned short *pbuf
;
375 const unsigned char *p
;
378 #if LCD_WIDTH >= 160 && LCD_HEIGHT >= 96
379 struct screen
*sc
= &screens
[l
];
381 if (sc
->getwidth() >= 160 && sc
->getheight() >= 96)
383 p
= "ABCDEFG abcdefg !?\" @#$%+'\n"
384 "HIJKLMN hijklmn 789 &_()-`\n"
385 "OPQRSTU opqrstu 456 §|{}/<\n"
386 "VWXYZ., vwxyz.,0123 ~=[]*>\n"
387 "ÀÁÂÃÄÅÆ ÌÍÎÏ ÈÉÊË ¢£¤¥¦§©®\n"
388 "àáâãäåæ ìíîï èéêë «»°ºª¹²³\n"
389 "ÓÒÔÕÖØ ÇÐÞÝß ÙÚÛÜ ¯±×÷¡¿µ·\n"
390 "òóôõöø çðþýÿ ùúûü ¼½¾¬¶¨:;";
392 pm
->default_lines
= 8;
393 pm
->max_line_len
= 26;
396 #endif /* LCD_WIDTH >= 160 && LCD_HEIGHT >= 96 */
398 p
= "ABCDEFG !?\" @#$%+'\n"
399 "HIJKLMN 789 &_()-`\n"
400 "OPQRSTU 456 §|{}/<\n"
401 "VWXYZ.,0123 ~=[]*>\n"
403 "abcdefg ¢£¤¥¦§©®¬\n"
404 "hijklmn «»°ºª¹²³¶\n"
405 "opqrstu ¯±×÷¡¿µ·¨\n"
408 "ÀÁÂÃÄÅÆ ÌÍÎÏ ÈÉÊË\n"
409 "àáâãäåæ ìíîï èéêë\n"
410 "ÓÒÔÕÖØ ÇÐÞÝß ÙÚÛÜ\n"
413 pm
->default_lines
= 4;
414 pm
->max_line_len
= 18;
420 p
= utf8decode(p
, &pbuf
[len
+1]);
421 if (pbuf
[len
+1] == '\n')
431 pbuf
[len
+1] = 0xFEFF; /* mark end of characters */
433 /* initialize parameters */
434 pm
->x
= pm
->y
= pm
->page
= 0;
441 struct keyboard_parameters
*pm
= ¶m
[l
];
442 struct screen
*sc
= &screens
[l
];
443 kbd_calc_params(pm
, sc
, &state
);
446 if (global_settings
.talk_menu
) /* voice UI? */
447 talk_spell(state
.text
, true); /* spell initial text */
451 /* These declarations are assigned to the screen on which the key
452 action occurred - pointers save a lot of space over array notation
453 when accessing the same array element countless times */
458 const int button_screen
= 0;
460 struct keyboard_parameters
*pm
;
463 state
.len_utf8
= utf8length(state
.text
);
467 /* declare scoped pointers inside screen loops - hide the
468 declarations from previous block level */
469 struct keyboard_parameters
*pm
= ¶m
[l
];
470 struct screen
*sc
= &screens
[l
];
472 kbd_draw_picker(pm
, sc
, &state
);
473 kbd_draw_edit_line(pm
, sc
, &state
);
474 #ifdef HAVE_TOUCHSCREEN
475 if (pm
->show_buttons
)
476 kbd_draw_buttons(pm
, sc
);
480 #ifdef HAVE_BUTTONBAR
481 /* draw the button bar */
482 gui_buttonbar_set(&buttonbar
, "Shift", "OK", "Del");
483 gui_buttonbar_draw(&buttonbar
);
489 state
.cur_blink
= !state
.cur_blink
;
492 #ifdef HAVE_MORSE_INPUT
493 state
.morse_mode
? CONTEXT_MORSE_INPUT
:
495 CONTEXT_KEYBOARD
, HZ
/2);
497 button_screen
= (get_action_statuscode(NULL
) & ACTION_REMOTE
) ? 1 : 0;
499 pm
= ¶m
[button_screen
];
500 sc
= &screens
[button_screen
];
501 #ifdef HAVE_TOUCHSCREEN
502 if (button
== ACTION_TOUCHSCREEN
)
503 button
= keyboard_touchscreen(pm
, sc
, &state
);
506 /* Remap some buttons to allow to move
507 * cursor in line edit mode and morse mode. */
509 #ifdef HAVE_MORSE_INPUT
511 #endif /* HAVE_MORSE_INPUT */
514 if (button
== ACTION_KBD_LEFT
)
515 button
= ACTION_KBD_CURSOR_LEFT
;
516 if (button
== ACTION_KBD_RIGHT
)
517 button
= ACTION_KBD_CURSOR_RIGHT
;
522 case ACTION_KBD_DONE
:
523 /* accepts what was entered and continues */
528 case ACTION_KBD_ABORT
:
533 case ACTION_KBD_PAGE_FLIP
:
534 #ifdef HAVE_MORSE_INPUT
535 if (state
.morse_mode
)
538 if (++pm
->page
>= pm
->pages
)
541 state
.changed
= CHANGED_PICKER
;
544 case ACTION_KBD_RIGHT
:
545 kbd_move_picker_horizontal(pm
, &state
, 1);
548 case ACTION_KBD_LEFT
:
549 kbd_move_picker_horizontal(pm
, &state
, -1);
552 case ACTION_KBD_DOWN
:
553 kbd_move_picker_vertical(pm
, &state
, 1);
557 kbd_move_picker_vertical(pm
, &state
, -1);
560 #ifdef HAVE_MORSE_INPUT
561 #ifdef KBD_TOGGLE_INPUT
562 case ACTION_KBD_MORSE_INPUT
:
563 state
.morse_mode
= !state
.morse_mode
;
564 state
.changed
= CHANGED_PICKER
;
568 struct keyboard_parameters
*pm
= ¶m
[l
];
570 pm
->main_y
= pm
->old_main_y
;
574 #endif /* KBD_TOGGLE_INPUT */
576 case ACTION_KBD_MORSE_SELECT
:
577 if (state
.morse_mode
&& state
.morse_reading
)
579 state
.morse_code
<<= 1;
580 if ((current_tick
- state
.morse_tick
) > HZ
/5)
581 state
.morse_code
|= 0x01;
584 #endif /* HAVE_MORSE_INPUT */
586 case ACTION_KBD_SELECT
:
587 /* select doubles as backspace in line_edit */
589 kbd_backspace(&state
);
591 #ifdef HAVE_MORSE_INPUT
592 if (state
.morse_mode
)
594 state
.morse_tick
= current_tick
;
596 if (!state
.morse_reading
)
598 state
.morse_reading
= true;
599 state
.morse_code
= 1;
603 #endif /* HAVE_MORSE_INPUT */
604 kbd_insert_selected(pm
, &state
);
607 case ACTION_KBD_BACKSPACE
:
608 kbd_backspace(&state
);
611 case ACTION_KBD_CURSOR_RIGHT
:
612 kbd_move_cursor(&state
, 1);
615 case ACTION_KBD_CURSOR_LEFT
:
616 kbd_move_cursor(&state
, -1);
620 #ifdef HAVE_MORSE_INPUT
621 if (state
.morse_reading
)
624 logf("Morse: 0x%02x", state
.morse_code
);
625 state
.morse_reading
= false;
627 for (j
= 0; morse_alphabets
[j
] != '\0'; j
++)
629 if (morse_codes
[j
] == state
.morse_code
)
633 if (morse_alphabets
[j
] == '\0')
635 logf("Morse code not found");
639 /* turn off hangul input */
640 state
.hangul
= false;
641 kbd_inschar(&state
, morse_alphabets
[j
]);
643 #endif /* HAVE_MORSE_INPUT */
647 if (default_event_handler(button
) == SYS_USB_CONNECTED
)
650 screens
[l
].setfont(FONT_SYSFIXED
);
656 if (button
!= ACTION_NONE
)
658 state
.cur_blink
= true;
660 if (global_settings
.talk_menu
) /* voice UI? */
662 if (state
.changed
== CHANGED_PICKER
)
666 talk_id(VOICE_EDIT
, false);
669 #ifdef HAVE_MORSE_INPUT
670 /* FIXME: We should talk something like Morse mode.. */
671 if (!state
.morse_mode
)
674 ch
= get_kbd_ch(pm
, pm
->x
, pm
->y
);
678 else if (state
.changed
== CHANGED_CURSOR
)
680 int c
= utf8seek(state
.text
, state
.editpos
);
681 kbd_spellchar(state
.text
[c
]);
683 else if (state
.changed
== CHANGED_TEXT
)
684 talk_spell(state
.text
, false); /* speak revised text */
689 #ifdef HAVE_BUTTONBAR
690 global_settings
.buttonbar
= buttonbar_config
;
694 splash(HZ
/2, ID2P(LANG_CANCEL
));
696 #if defined(HAVE_MORSE_INPUT) && defined(KBD_TOGGLE_INPUT)
697 if (global_settings
.morse_input
!= state
.morse_mode
)
699 global_settings
.morse_input
= state
.morse_mode
;
702 #endif /* HAVE_MORSE_INPUT && KBD_TOGGLE_INPUT */
706 screens
[l
].setfont(FONT_UI
);
707 viewportmanager_theme_undo(l
, false);
712 static void kbd_calc_params(struct keyboard_parameters
*pm
,
713 struct screen
*sc
, struct edit_state
*state
)
716 const unsigned char *p
;
717 unsigned short ch
, *pbuf
;
718 int icon_w
, sc_w
, sc_h
, w
;
720 #ifdef HAVE_TOUCHSCREEN
722 bool flippage_button
= false;
723 pm
->show_buttons
= (sc
->screen_type
== SCREEN_MAIN
&&
724 (touchscreen_get_mode() == TOUCHSCREEN_POINT
));
727 pm
->curfont
= pm
->default_lines
? FONT_SYSFIXED
: FONT_UI
;
728 font
= font_get(pm
->curfont
);
729 pm
->font_h
= font
->height
;
731 /* check if FONT_UI fits the screen */
732 if (2*pm
->font_h
+ 3 + BUTTONBAR_HEIGHT
> sc
->getheight())
734 pm
->curfont
= FONT_SYSFIXED
;
735 font
= font_get(FONT_SYSFIXED
);
736 pm
->font_h
= font
->height
;
738 #ifdef HAVE_TOUCHSCREEN
739 pm
->font_h
= GRID_SIZE(sc
->screen_type
, pm
->font_h
);
742 /* find max width of keyboard glyphs.
743 * since we're going to be adding spaces,
744 * max width is at least their width */
745 pm
->font_w
= font_get_width(font
, ' ');
746 for (pbuf
= pm
->kbd_buf
; *pbuf
!= 0xFEFF; pbuf
+= i
)
748 for (i
= 0; ++i
<= *pbuf
; )
750 w
= font_get_width(font
, pbuf
[i
]);
756 /* Find max width for text string */
757 pm
->text_w
= pm
->font_w
;
761 p
= utf8decode(p
, &ch
);
762 w
= font_get_width(font
, ch
);
767 #ifdef HAVE_TOUCHSCREEN
768 pm
->font_w
= GRID_SIZE(sc
->screen_type
, pm
->font_w
);
770 /* calculate how many characters to put in a row. */
771 icon_w
= get_icon_width(sc
->screen_type
);
772 sc_w
= sc
->getwidth();
773 if (pm
->font_w
< sc_w
/ pm
->max_line_len
)
774 pm
->font_w
= sc_w
/ pm
->max_line_len
;
775 pm
->max_chars
= sc_w
/ pm
->font_w
;
776 pm
->max_chars_text
= (sc_w
- icon_w
* 2 - 2) / pm
->text_w
;
777 if (pm
->max_chars_text
< 3 && icon_w
> pm
->text_w
)
778 pm
->max_chars_text
= sc_w
/ pm
->text_w
- 2;
780 /* calculate pm->pages and pm->lines */
781 sc_h
= sc
->getheight();
782 #ifdef HAVE_TOUCHSCREEN
783 /* add space for buttons */
784 if (pm
->show_buttons
)
786 /* reserve place for OK/Del/Cancel buttons, use ui font for them */
787 button_h
= GRID_SIZE(sc
->screen_type
, sc
->getcharheight());
788 sc_h
-= MAX(MIN_GRID_SIZE
*2, button_h
);
792 pm
->lines
= (sc_h
- BUTTONBAR_HEIGHT
) / pm
->font_h
- 1;
794 if (pm
->default_lines
&& pm
->lines
> pm
->default_lines
)
795 pm
->lines
= pm
->default_lines
;
797 pm
->keyboard_margin
= sc_h
- BUTTONBAR_HEIGHT
798 - (pm
->lines
+1)*pm
->font_h
;
800 if (pm
->keyboard_margin
< 3 && pm
->lines
> 1)
803 pm
->keyboard_margin
+= pm
->font_h
;
806 if (pm
->keyboard_margin
> DEFAULT_MARGIN
)
807 pm
->keyboard_margin
= DEFAULT_MARGIN
;
810 for (pbuf
= pm
->kbd_buf
; (i
= *pbuf
) != 0xFEFF; pbuf
+= i
+ 1)
811 total_lines
+= (i
? (i
+ pm
->max_chars
- 1) / pm
->max_chars
: 1);
813 pm
->pages
= (total_lines
+ pm
->lines
- 1) / pm
->lines
;
814 pm
->lines
= (total_lines
+ pm
->pages
- 1) / pm
->pages
;
815 #ifdef HAVE_TOUCHSCREEN
816 if (pm
->pages
> 1 && pm
->show_buttons
&& !flippage_button
)
818 /* add space for flip page button */
820 flippage_button
= true;
824 if (pm
->page
>= pm
->pages
)
825 pm
->x
= pm
->y
= pm
->page
= 0;
827 pm
->main_y
= pm
->font_h
*pm
->lines
+ pm
->keyboard_margin
;
828 pm
->keyboard_margin
-= pm
->keyboard_margin
/2;
829 #ifdef HAVE_TOUCHSCREEN
830 /* flip page button is put between piker and edit line */
832 pm
->main_y
+= button_h
;
835 #ifdef HAVE_MORSE_INPUT
836 pm
->old_main_y
= sc_h
- pm
->font_h
- BUTTONBAR_HEIGHT
;
837 if (state
->morse_mode
)
840 pm
->main_y
= pm
->old_main_y
;
846 static void kbd_draw_picker(struct keyboard_parameters
*pm
,
847 struct screen
*sc
, struct edit_state
*state
)
850 #ifdef HAVE_MORSE_INPUT
851 if (state
->morse_mode
)
853 const int w
= 6, h
= 8; /* sysfixed font width, height */
855 int sc_w
= sc
->getwidth(), sc_h
= pm
->main_y
- pm
->keyboard_margin
- 1;
857 /* Draw morse code screen with sysfont */
858 sc
->setfont(FONT_SYSFIXED
);
863 /* Draw morse code table with code descriptions. */
864 for (i
= 0; morse_alphabets
[i
] != '\0'; i
++)
868 outline
[0] = morse_alphabets
[i
];
869 sc
->putsxy(x
, y
, outline
);
871 morse_code
= morse_codes
[i
];
872 for (j
= 0; morse_code
> 0x01; morse_code
>>= 1)
876 morse_code
= morse_codes
[i
];
877 for (; morse_code
> 0x01; morse_code
>>= 1)
880 if (morse_code
& 0x01)
881 sc
->fillrect(x
, y
+ 2, 3, 4);
883 sc
->fillrect(x
, y
+ 3, 1, 2);
899 #endif /* HAVE_MORSE_INPUT */
907 sc
->setfont(pm
->curfont
);
909 for (j
= 0; j
< pm
->lines
; j
++)
911 for (i
= 0; i
< pm
->max_chars
; i
++)
913 ch
= get_kbd_ch(pm
, i
, j
);
914 utf8
= utf8encode(ch
, outline
);
917 sc
->getstringsize(outline
, &w
, &h
);
918 sc
->putsxy(i
*pm
->font_w
+ (pm
->font_w
-w
) / 2,
919 j
*pm
->font_h
+ (pm
->font_h
-h
) / 2, outline
);
925 /* highlight the key that has focus */
926 sc
->set_drawmode(DRMODE_COMPLEMENT
);
927 sc
->fillrect(pm
->font_w
*pm
->x
, pm
->font_h
*pm
->y
,
928 pm
->font_w
, pm
->font_h
);
929 sc
->set_drawmode(DRMODE_SOLID
);
934 static void kbd_draw_edit_line(struct keyboard_parameters
*pm
,
935 struct screen
*sc
, struct edit_state
*state
)
939 int i
= 0, j
= 0, icon_w
, w
;
940 int sc_w
= sc
->getwidth();
941 int y
= pm
->main_y
- pm
->keyboard_margin
;
942 int text_margin
= (sc_w
- pm
->text_w
* pm
->max_chars_text
) / 2;
944 /* Clear text area one pixel above separator line so any overdraw
946 screen_clear_area(sc
, 0, y
- 1, sc_w
, pm
->font_h
+ 6);
948 sc
->hline(0, sc_w
- 1, y
);
950 /* write out the text */
951 sc
->setfont(pm
->curfont
);
953 pm
->leftpos
= MAX(0, MIN(state
->len_utf8
, state
->editpos
+ 2)
954 - pm
->max_chars_text
);
955 pm
->curpos
= state
->editpos
- pm
->leftpos
;
956 utf8
= state
->text
+ utf8seek(state
->text
, pm
->leftpos
);
958 while (*utf8
&& i
< pm
->max_chars_text
)
960 j
= utf8seek(utf8
, 1);
961 strlcpy(outline
, utf8
, j
+1);
962 sc
->getstringsize(outline
, &w
, NULL
);
963 sc
->putsxy(text_margin
+ i
*pm
->text_w
+ (pm
->text_w
-w
)/2,
964 pm
->main_y
, outline
);
969 icon_w
= get_icon_width(sc
->screen_type
);
972 /* Draw nicer bitmap arrow if room, else settle for "<". */
973 if (text_margin
>= icon_w
)
975 screen_put_icon_with_offset(sc
, 0, 0,
976 (text_margin
- icon_w
) / 2,
977 pm
->main_y
, Icon_Reverse_Cursor
);
981 sc
->getstringsize("<", &w
, NULL
);
982 sc
->putsxy(text_margin
- w
, pm
->main_y
, "<");
986 if (state
->len_utf8
- pm
->leftpos
> pm
->max_chars_text
)
988 /* Draw nicer bitmap arrow if room, else settle for ">". */
989 if (text_margin
>= icon_w
)
991 screen_put_icon_with_offset(sc
, 0, 0,
992 sc_w
- (text_margin
+ icon_w
) / 2,
993 pm
->main_y
, Icon_Cursor
);
997 sc
->putsxy(sc_w
- text_margin
, pm
->main_y
, ">");
1002 i
= text_margin
+ pm
->curpos
* pm
->text_w
;
1004 if (state
->cur_blink
)
1005 sc
->vline(i
, pm
->main_y
, pm
->main_y
+ pm
->font_h
- 1);
1007 if (state
->hangul
) /* draw underbar */
1008 sc
->hline(i
- pm
->text_w
, i
, pm
->main_y
+ pm
->font_h
- 1);
1012 sc
->set_drawmode(DRMODE_COMPLEMENT
);
1013 sc
->fillrect(0, y
+ 2, sc_w
, pm
->font_h
+ 2);
1014 sc
->set_drawmode(DRMODE_SOLID
);
1018 #ifdef HAVE_TOUCHSCREEN
1019 static void kbd_draw_buttons(struct keyboard_parameters
*pm
, struct screen
*sc
)
1022 int button_h
, text_h
, text_y
;
1023 int sc_w
= sc
->getwidth(), sc_h
= sc
->getheight();
1024 viewport_set_defaults(&vp
, sc
->screen_type
);
1025 vp
.flags
|= VP_FLAG_ALIGN_CENTER
;
1026 sc
->set_viewport(&vp
);
1027 text_h
= sc
->getcharheight();
1028 button_h
= GRID_SIZE(sc
->screen_type
, text_h
);
1029 text_y
= (button_h
- text_h
) / 2 + 1;
1033 vp
.height
= button_h
;
1037 /* button to flip page. */
1038 vp
.y
= pm
->lines
*pm
->font_h
;
1039 sc
->hline(0, sc_w
- 1, 0);
1040 sc
->putsxy(0, text_y
, ">");
1042 /* OK/Del/Cancel buttons */
1043 button_h
= MAX(MIN_GRID_SIZE
*2, button_h
);
1044 text_y
= (button_h
- text_h
) / 2 + 1;
1045 vp
.y
= sc_h
- button_h
- 1;
1046 vp
.height
= button_h
;
1047 sc
->hline(0, sc_w
- 1, 0);
1049 sc
->putsxy(0, text_y
, str(LANG_KBD_OK
));
1051 sc
->vline(0, 0, button_h
);
1052 sc
->putsxy(0, text_y
, str(LANG_KBD_DELETE
));
1054 sc
->vline(0, 0, button_h
);
1055 sc
->putsxy(0, text_y
, str(LANG_KBD_CANCEL
));
1056 sc
->set_viewport(NULL
);
1059 static int keyboard_touchscreen(struct keyboard_parameters
*pm
,
1060 struct screen
*sc
, struct edit_state
*state
)
1063 const int button
= action_get_touchscreen_press(&x
, &y
);
1064 const int sc_w
= sc
->getwidth(), sc_h
= sc
->getheight();
1065 int button_h
= MAX(MIN_GRID_SIZE
*2, sc
->getcharheight());
1066 #ifdef HAVE_MORSE_INPUT
1067 if (state
->morse_mode
&& y
< pm
->main_y
- pm
->keyboard_margin
)
1069 /* don't return ACTION_NONE since it has effect in morse mode. */
1070 return button
== BUTTON_TOUCHSCREEN
? ACTION_KBD_SELECT
:
1071 button
& BUTTON_REL
? ACTION_KBD_MORSE_SELECT
: ACTION_STD_OK
;
1078 if (y
< pm
->lines
*pm
->font_h
)
1080 if (x
/pm
->font_w
< pm
->max_chars
)
1083 state
->changed
= CHANGED_PICKER
;
1084 pm
->x
= x
/pm
->font_w
;
1085 pm
->y
= y
/pm
->font_h
;
1086 pm
->line_edit
= false;
1087 if (button
== BUTTON_REL
)
1088 return ACTION_KBD_SELECT
;
1091 else if (y
< pm
->main_y
- pm
->keyboard_margin
)
1093 /* button to flip page */
1094 if (button
== BUTTON_REL
)
1095 return ACTION_KBD_PAGE_FLIP
;
1097 else if (y
< sc_h
- button_h
)
1100 if (button
& (BUTTON_REPEAT
|BUTTON_REL
))
1103 return ACTION_KBD_CURSOR_LEFT
;
1105 return ACTION_KBD_CURSOR_RIGHT
;
1110 /* OK/Del/Cancel button */
1111 if (button
== BUTTON_REL
)
1114 return ACTION_KBD_DONE
;
1115 else if (x
< (sc_w
/3) * 2)
1116 return ACTION_KBD_BACKSPACE
;
1118 return ACTION_KBD_ABORT
;
1125 /* inserts the selected char */
1126 static void kbd_insert_selected(struct keyboard_parameters
*pm
,
1127 struct edit_state
*state
)
1129 /* find input char */
1130 unsigned short ch
= get_kbd_ch(pm
, pm
->x
, pm
->y
);
1132 /* check for hangul input */
1133 if (ch
>= 0x3131 && ch
<= 0x3163)
1139 state
->hlead
= state
->hvowel
= state
->htail
= 0;
1140 state
->hangul
= true;
1147 else if (!state
->htail
)
1153 /* previous hangul complete */
1154 /* check whether tail is actually lead of next char */
1155 tmp
= hangul_join(state
->htail
, ch
, 0);
1159 tmp
= hangul_join(state
->hlead
, state
->hvowel
, 0);
1161 kbd_inschar(state
, tmp
);
1162 /* insert dummy char */
1163 kbd_inschar(state
, ' ');
1164 state
->hlead
= state
->htail
;
1170 state
->hvowel
= state
->htail
= 0;
1175 /* combine into hangul */
1176 tmp
= hangul_join(state
->hlead
, state
->hvowel
, state
->htail
);
1185 state
->hvowel
= state
->htail
= 0;
1191 state
->hangul
= false;
1195 kbd_inschar(state
, ch
);
1198 static void kbd_backspace(struct edit_state
*state
)
1205 else if (state
->hvowel
)
1208 state
->hangul
= false;
1216 ch
= hangul_join(state
->hlead
, state
->hvowel
, state
->htail
);
1219 kbd_inschar(state
, ch
);
1223 static void kbd_move_cursor(struct edit_state
*state
, int dir
)
1225 state
->hangul
= false;
1226 state
->editpos
+= dir
;
1228 if (state
->editpos
>= 0 && state
->editpos
<= state
->len_utf8
)
1230 state
->changed
= CHANGED_CURSOR
;
1234 state
->editpos
-= dir
;
1235 #if CONFIG_CODEC == SWCODEC
1236 if (global_settings
.talk_menu
)
1237 pcmbuf_beep(1000, 150, 1500);
1242 static void kbd_move_picker_horizontal(struct keyboard_parameters
*pm
,
1243 struct edit_state
*state
, int dir
)
1245 state
->changed
= CHANGED_PICKER
;
1251 pm
->page
= pm
->pages
- 1;
1252 pm
->x
= pm
->max_chars
- 1;
1254 else if (pm
->x
>= pm
->max_chars
)
1256 if (++pm
->page
>= pm
->pages
)
1262 static void kbd_move_picker_vertical(struct keyboard_parameters
*pm
,
1263 struct edit_state
*state
, int dir
)
1265 state
->changed
= CHANGED_PICKER
;
1267 #ifdef HAVE_MORSE_INPUT
1268 if (state
->morse_mode
)
1270 pm
->line_edit
= !pm
->line_edit
;
1273 #endif /* HAVE_MORSE_INPUT */
1278 pm
->y
= (dir
> 0 ? 0 : pm
->lines
- 1);
1279 pm
->line_edit
= false;
1281 else if (pm
->y
< 0 || pm
->y
>= pm
->lines
)
1283 pm
->line_edit
= true;