Index: apps/plugins/CATEGORIES =================================================================== --- apps/plugins/CATEGORIES (révision 18346) +++ apps/plugins/CATEGORIES (copie de travail) @@ -33,6 +33,7 @@ jewels,games jpeg,viewers keybox,apps +keypad_demo,demos lamp,apps logo,demos mandelbrot,demos Index: apps/plugins/keypad_demo.c =================================================================== --- apps/plugins/keypad_demo.c (révision 0) +++ apps/plugins/keypad_demo.c (révision 0) @@ -0,0 +1,552 @@ +#include "plugin.h" +#include "pluginlib_actions.h" + +#ifndef PARENT +PLUGIN_HEADER +static const struct plugin_api* rb; +#endif + +const struct button_mapping *plugin_contexts[] = + {generic_directions, generic_actions}; + +/* Mapping */ +#define EXIT PLA_QUIT +#define MENU PLA_MENU +#define ACCEPT PLA_START +#define SWITCH PLA_FIRE +#define LONG_SWITCH PLA_FIRE_REPEAT +#define LEFT PLA_LEFT +#define LEFT_REPEAT PLA_LEFT_REPEAT +#define RIGHT PLA_RIGHT +#define RIGHT_REPEAT PLA_RIGHT_REPEAT +#define UP PLA_UP +#define UP_REPEAT PLA_UP_REPEAT +#define DOWN PLA_DOWN +#define DOWN_REPEAT PLA_DOWN_REPEAT + +/* Defines */ +#define EOS 1 +#define NULL_CHAR '\0' +#define MAX_STR_LEN 1024 +#define MAX_CHAR_ID 126 +#define NB_LISTS 4 +#define MAX_CHARS_IN_LIST 33 + +#define LEFT_MARGIN 5 +#define RIGHT_MARGIN 5 + +#define PLUGIN_X 0 +#define PLUGIN_Y 0 +#define PLUGIN_W LCD_WIDTH + +struct char_data { + int list_id; + int id_in_list; +} reversed_chars_list[MAX_CHAR_ID + 1]; + +const int LIST_SIZE[NB_LISTS] = {26, 26, 10, 33}; + +const char chars_lists[NB_LISTS][MAX_CHARS_IN_LIST] = { + {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}, + {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}, + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}, + {' ', '.', ',', ';', ':', '!', '?', 39, 34, '@', '#', '$', '%', '&', '(', + ')', '{', '}', '[', ']', '|', 92, 47, '+', '-', '*', '<', '=', '>', '^', + '~', '_', 96} +};/* 39: ' 47: / 34: " 96: ` 92: \ */ + +bool refresh_typing_screen = true; +bool accept = false, cancel = false, usb = false; + +bool insert = true; +bool preserve_words = true; +bool flush_string = false; + +static int max_length; +static int last_char, cur_char; +static int char_list, char_id; + +/* chars buffer */ +#define BUFF_INIT_SIZE 2 +#define LONGEST_LIST_SIZE 26 +char buff_init[BUFF_INIT_SIZE + EOS] = "> "; +char chars_buffer[BUFF_INIT_SIZE + LONGEST_LIST_SIZE + EOS]; + +char string[MAX_STR_LEN + EOS]; + +inline int string_len(void) { + return (last_char + 1); +} + +void display_help (void) { + rb->lcd_clear_display(); + rb->lcd_puts(0, 0, "Typing help :"); + rb->lcd_puts(0, 1, "Start (A):"); + rb->lcd_puts(0, 2, " Switch between insert & replace"); + rb->lcd_puts(0, 3, "Up/Down:"); + rb->lcd_puts(0, 4, " Edit current char"); + rb->lcd_puts(0, 5, "Menu:"); + rb->lcd_puts(0, 6, " Delete char"); + rb->lcd_puts(0, 7, " - Replace mode - "); + rb->lcd_puts(0, 8, "Select:"); + rb->lcd_puts(0, 9, " Switch between uppercase & lowercase"); + rb->lcd_puts(0, 10, "Long select:"); + rb->lcd_puts(0, 11, " Switch between letters & special chars"); + rb->lcd_puts(0, 12, " - Insert mode - "); + rb->lcd_puts(0, 13, "Select:"); + rb->lcd_puts(0, 14, " Insert one char left"); + rb->lcd_puts(0, 15, "Long select:"); + rb->lcd_puts(0, 16, " Switch between A-Z/a-z/0-9 & .-?/' '-_"); + rb->lcd_update(); + refresh_typing_screen = false; +} + +void load_edit_menu(void) { + int menu_item; + MENUITEM_STRINGLIST(typing_menu, + " - Typing menu - ", + NULL, + "Accept", + "Cancel & return", + "Display help text", + "Switch between insert/replace modes", + "Switch between split/reserve words modes"); + menu_item = rb->do_menu(&typing_menu, &menu_item, NULL, false); + + switch(menu_item) { + case 0: + accept = true; + break; + case 1: + cancel = true; + break; + case 2: + display_help(); + break; + case 3: + insert = !insert; + rb->splashf(HZ, "Insert mode: %s", insert ? "on !" : "off !"); + break; + case 4: + preserve_words = !preserve_words; + rb->splashf(HZ, "Split words: %s", + !preserve_words ? "on !" : "off !"); + break; + case MENU_ATTACHED_USB: + usb = true; + break; + } +} + +void move(int dir) +{ + if (dir == 1) { + if (insert && last_char == -1) return; + + cur_char++; + if (insert && cur_char == string_len()) + cur_char = -1; + else if (!insert && cur_char == max_length) + cur_char = 0; + + if (cur_char > last_char) { + if (last_char >= 0) + string[cur_char] = string[last_char]; + else + string[cur_char] = ' '; + last_char++; + } + } + if (dir == -1) { + cur_char--; + + if ((cur_char == -1 && !insert) || cur_char < -1) + cur_char = last_char; + } +} + +void frame_char(int index, char frame) +{ + if (index == -1) { + rb->memmove(string + 1, string, string_len() + EOS); + string[index + 1] = frame; + last_char++; + } + else { + if (index <= cur_char) cur_char++; + + rb->memmove(string + index + 1, string + index, + string_len() - index + EOS); + last_char++; + + index++; + rb->memmove(string + index + 2, string + index + 1, + string_len() - (index + 1) + EOS); + last_char++; + + string[index - 1] = string[index + 1] = frame; + } +} + +void unframe_char(int index) +{ + if (index == -1) { + rb->memmove(string, string + 1, string_len() + EOS); + last_char--; + } + else { + if (index <= cur_char) cur_char--; + rb->memmove(string + index - 1, string + index, + string_len() - index + EOS); + last_char--; + + index--; + rb->memmove(string + index + 1, string + index + 2, + string_len() - (index + 1) + EOS); + last_char--; + } +} + +void remove_char(int index) +{ + rb->memmove(string + index, string + index + 1, + string_len() - index + EOS); + + if (index <= cur_char) + cur_char--; + last_char--; +} + +void insert_char(int index, char c) +{ + bool negative_id = (index == -1); + + if (negative_id) index++; + rb->memmove(string + index + 1, string + index, + string_len() - index + EOS); + string[index] = c; + if (negative_id) index--; + + if (index <= cur_char) + cur_char++; + last_char++; +} + +int get_word_lw(char* str, int pos, int max_pos, bool get_width) { + char next_c; + int nb_chars = 0, str_w = 0; + do { + next_c = str[pos]; + str_w += rb->font_get_width(rb->font_get(FONT_UI), str[pos]); + + pos++; + nb_chars++; + } while (pos < max_pos && next_c != NULL_CHAR && next_c != ' '); + + return (get_width ? str_w : nb_chars); +} + +int draw_string(char* str, int len, int x, int y, int l_h, int max_l_w) { + int line_w = 0; + + char line[256]; + int line_id = 0; + int pos_in_l = 0; + int pos_in_str = 0; + + bool next_line; + int word_w, word_l; + + bool long_word = false; + + x += LEFT_MARGIN; + max_l_w -= (LEFT_MARGIN + RIGHT_MARGIN); + + while (pos_in_str < len) { + line_w = 0; + pos_in_l = 0; + next_line = false; + + while (!next_line && pos_in_str < len) { + word_w = -1; + word_l = -1; + + if (preserve_words && !long_word) { + word_l = get_word_lw(str, pos_in_str, len, 0); + word_w = get_word_lw(str, pos_in_str, len, 1); + } + + if (word_w >= max_l_w) + long_word = true; + if (str[pos_in_str] == ' ') + long_word = false; + + if (!preserve_words || long_word) { + word_l = 1; + word_w = rb->font_get_width(rb->font_get(FONT_UI), + str[pos_in_str]); + } + + if (line_w + word_w < max_l_w) { + rb->memcpy(&line[pos_in_l], &str[pos_in_str], word_l); + + /* TODO: Do not count framing chars + if (pos_in_str != cur_char + 1) + if (!(!insert && pos_in_str == cur_char - 1)) */ + line_w += word_w; + + pos_in_l += word_l; + pos_in_str += word_l; + } + else { + next_line = true; + } + + if (str[pos_in_str] == '\n') { + pos_in_str++; + next_line = true; + } + } + + line[pos_in_l] = NULL_CHAR; + rb->lcd_putsxy(x, y + l_h * line_id, line); + + line_id++; + } + + if (len == 0) line_id++; + return line_id * l_h; +} + +int get_list_id(int rb_utf8_char_id) +{ + return reversed_chars_list[rb_utf8_char_id].list_id; +} + +int get_pos_in_list(int rb_utf8_char_id) +{ + return reversed_chars_list[rb_utf8_char_id].id_in_list; +} + +int get_user_entry(char* str, int max_str_len) +{ + rb->memset(string, NULL_CHAR, MAX_STR_LEN); + rb->strcpy(string, str); + + max_length = max_str_len - 1; + cur_char = last_char = rb->strlen(string) - 1; + + if (last_char == -1) { + string[0] = ' '; + cur_char = last_char = 0; + } + + accept = false; cancel = false; usb = false; + int button = -1, last_button = -1; + + int line_height, line_spacing; + line_spacing = 2; + line_height = rb->font_get(FONT_UI)->height + line_spacing; + + int BUFFER_H; + int TXT_AREA_H = LCD_HEIGHT; + int TXT_AREA_Y = BUFFER_H = PLUGIN_Y + line_height; + + /* build reversed chars list in O(nb of chars)*/ + int list, pos; + for(list = 0; list < NB_LISTS; list++) { + for(pos = 0; pos < LIST_SIZE[list]; pos++) { + int rb_utf8_code = chars_lists[list][pos]; + reversed_chars_list[rb_utf8_code].list_id = list; + reversed_chars_list[rb_utf8_code].id_in_list = pos; + } + } + + while(!accept && !cancel && !usb) { + char_list = get_list_id(string[cur_char]); + char_id = get_pos_in_list(string[cur_char]); + + /* chars buffer */ + int buff_pos; + int buff_char; + rb->strcpy(chars_buffer, buff_init); + for(buff_pos = 0; buff_pos < LIST_SIZE[char_list]; buff_pos++) { + buff_char = chars_lists[char_list] + [(char_id + buff_pos) % LIST_SIZE[char_list]]; + chars_buffer[buff_pos + BUFF_INIT_SIZE] = buff_char; + } + /* Also functional -- uses memmoves instead of a loop. + int r_buffer_size = char_id; + int l_buffer_size = LIST_SIZE[char_list] - char_id; + rb->memmove(chars_buffer, + buff_init, + BUFF_INIT_SIZE); + rb->memmove(chars_buffer + BUFF_INIT_SIZE, + chars_lists[char_list] + char_id, + l_buffer_size); + rb->memmove(chars_buffer + BUFF_INIT_SIZE + l_buffer_size, + chars_lists[char_list], + r_buffer_size); + */ + chars_buffer[BUFF_INIT_SIZE + LIST_SIZE[char_list]] = NULL_CHAR; + + if (!refresh_typing_screen) goto jump_refresh; + + /* Add '| |' or '|'(on the right) around cur char*/ + if (insert) insert_char(cur_char + 1, '|'); + if (!insert) frame_char(cur_char, '|'); + + /*Clear screen */ + rb->lcd_clear_display(); + /* Print buffer + hline + string */ + rb->lcd_putsxy(PLUGIN_X, PLUGIN_Y, chars_buffer); + draw_string(string, string_len(), PLUGIN_X, TXT_AREA_Y, + line_height, LCD_WIDTH - PLUGIN_X); + rb->lcd_hline(2, LCD_WIDTH - 2, line_height - line_spacing / 2); + + /* Trying to minimise the amount of screen updated... */ + /* TODO: reduce area height */ + rb->lcd_update_rect(PLUGIN_X, PLUGIN_Y, + PLUGIN_W, BUFFER_H + TXT_AREA_H); + + /* Remove '| |' or '|' from around cur char*/ + if (insert) remove_char(cur_char + 1); + if (!insert) unframe_char(cur_char); + + jump_refresh: + refresh_typing_screen = true; + + /* button actions */ + button = pluginlib_getaction(rb, TIMEOUT_BLOCK, plugin_contexts, 2); + switch(button) { + case EXIT: + load_edit_menu(); + break; + + case LEFT: + case LEFT_REPEAT: + if (last_char == -1) break; + move(-1); + break; + case RIGHT: + case RIGHT_REPEAT: + move(1); + break; + + case UP: + case UP_REPEAT: + if (cur_char == -1) break; + if (char_id > 0) + char_id--; + else + char_id = LIST_SIZE[char_list] - 1; + flush_string = true; + break; + case DOWN: + case DOWN_REPEAT: + if (cur_char == -1) break; + if (char_id < LIST_SIZE[char_list] - 1) + char_id++; + else + char_id = 0; + flush_string = true; + break; + + case SWITCH: /* center -> switch between lists or insert */ + if (insert == true) { + insert_char(cur_char, + (cur_char == -1) ? 'A' : string[cur_char]); + break; + } + if (cur_char == -1) break; + + switch(char_list) { + case 0: + char_list = 1; + break; + case 1: + char_list = 0; + break; + case 2: + char_list = 3; + break; + case 3: + char_list = 2; + break; + } + + flush_string = true; + break; + case LONG_SWITCH: + if (cur_char == -1) break; + /* This event should only happen once on long press */ + if (button != last_button) { + if (insert) remove_char(cur_char); /* inserted char */ + char_list = (char_list + ((insert) ? 1 : 2)) % NB_LISTS; + } + flush_string = true; + break; + + case MENU: + if (cur_char == -1) break; + remove_char(cur_char); + break; + + case ACCEPT: + accept = true; + break; + + default: + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) + usb = true; + break; + } + last_button = button; + + if (flush_string) { + char_id = char_id % LIST_SIZE[char_list]; + string[cur_char] = chars_lists[char_list][char_id]; + } + + flush_string = false; + } + + if (accept) { + rb->memset(str, 0, max_length); + rb->strcpy(str, string); + accept = false; + } + + return 0; +} + +#ifndef PARENT +enum plugin_status plugin_start +(const struct plugin_api* api, const void* parameter) +{ + (void)parameter; + rb = api; + + max_length = MAX_STR_LEN; + + return get_user_entry(string, max_length); + } +#endif + +/* Undef mapping consts to not create problems with other plugins */ +#undef EXIT +#undef MENU +#undef INSERT +#undef ACCEPT +#undef SWITCH +#undef LONG_SWITCH +#undef LEFT +#undef LEFT_REPEAT +#undef RIGHT +#undef RIGHT_REPEAT +#undef UP +#undef UP_REPEAT +#undef DOWN +#undef DOWN_REPEAT Index: apps/plugins/keypad_demo.h =================================================================== --- apps/plugins/keypad_demo.h (révision 0) +++ apps/plugins/keypad_demo.h (révision 0) @@ -0,0 +1,2 @@ +#define PARENT +#include "keypad_demo.c" Index: apps/plugins/SOURCES =================================================================== --- apps/plugins/SOURCES (révision 18346) +++ apps/plugins/SOURCES (copie de travail) @@ -158,3 +158,4 @@ #endif /* m:robe 500 */ md5sum.c +keypad_demo.c