Index: apps/plugins/CATEGORIES =================================================================== --- apps/plugins/CATEGORIES (révision 17722) +++ apps/plugins/CATEGORIES (copie de travail) @@ -59,6 +59,7 @@ rockblox,games rockbox_flash,viewers rockboy,viewers +rockedit,apps rocklife,games rockpaint,apps search,viewers Index: apps/plugins/viewers.config =================================================================== --- apps/plugins/viewers.config (révision 17722) +++ apps/plugins/viewers.config (copie de travail) @@ -2,6 +2,7 @@ txt,viewers/viewer,1 nfo,viewers/viewer,1 txt,apps/text_editor,2 +txt,apps/rockedit,1 jpg,viewers/jpeg,2 jpe,viewers/jpeg,2 jpeg,viewers/jpeg,2 Index: apps/plugins/typing.c =================================================================== --- apps/plugins/typing.c (révision 0) +++ apps/plugins/typing.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 26 + +#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, 18, 25}; + +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->splash(HZ, "Insert mode: %s", insert ? "on !" : "off !"); + break; + case 4: + preserve_words = !preserve_words; + rb->splash(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 interfere 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 Modification de propriétés sur apps/plugins/typing.c ___________________________________________________________________ Nom : svn:executable + * Index: apps/plugins/typing.h =================================================================== --- apps/plugins/typing.h (révision 0) +++ apps/plugins/typing.h (révision 0) @@ -0,0 +1,2 @@ +#define PARENT +#include "typing.c" Modification de propriétés sur apps/plugins/typing.h ___________________________________________________________________ Nom : svn:executable + * Index: apps/plugins/rockedit.c =================================================================== --- apps/plugins/rockedit.c (révision 0) +++ apps/plugins/rockedit.c (révision 0) @@ -0,0 +1,287 @@ +#include "plugin.h" +#include "pluginlib_actions.h" + +PLUGIN_HEADER +static const struct plugin_api* rb; +#include "typing.h" + +/* Mapping */ +#define EXIT PLA_QUIT +#define MENU PLA_MENU +#define ACCEPT PLA_START +#define SELECT PLA_FIRE +#define UP PLA_UP +#define UP_REPEAT PLA_UP_REPEAT +#define DOWN PLA_DOWN +#define DOWN_REPEAT PLA_DOWN_REPEAT + +#define EOS 1 +#define MAX_LINES 128 +#define MAX_CHARS_PER_LINE 512 - 1 + +#define TOP_MARGIN 2 + +#define EXIT_MENU 0 +#define TEXT_MENU 1 + +int line_height; +int nb_lines_shown; +int line_spacing = 1; + +int cur_line_id = 0; +int lines_count = 1; + +int output_file; +bool file_loaded = false; +char fname[MAX_CHARS_PER_LINE]; + +char cur_line[MAX_CHARS_PER_LINE + 1]; +char lines[MAX_LINES][MAX_CHARS_PER_LINE + 1]; +/* +1 in MAX_CHARS_PER_LINE is for inserting '|' */ + +bool exit_editor = false; +bool refresh_editor_screen = true; + +void init(void) +{ + int line; + for(line = 0; line < lines_count; line++) { + rb->memset(lines[line], 0, MAX_CHARS_PER_LINE + 1); + } + rb->lcd_set_backdrop(NULL); + rb->lcd_set_background(LCD_BLACK); +} + +void remove_line(int index) +{ + if (cur_line_id == lines_count - 1) cur_line_id--; + if (index > 0 && index < lines_count) + rb->memmove(lines[index], lines[index + 1], + (lines_count - index) * (MAX_CHARS_PER_LINE + 1)); + lines_count--; +} + +void insert_line(int index) { + if (index < MAX_LINES - 1) { + int line_id; + for(line_id = lines_count; line_id >= index; line_id--) + rb->memmove(lines[line_id + 1], lines[line_id], + MAX_CHARS_PER_LINE); + } + rb->memset(lines[index], 0, MAX_CHARS_PER_LINE + 1); + + cur_line_id = index; + lines_count++; +} + +void open_file(bool ask_path) { + if (ask_path) get_user_entry(fname, MAX_CHARS_PER_LINE); + init(); + + int file; + file = rb->open(fname, O_RDONLY); + if (file > 0) + file_loaded = true; + else { + rb->splash(HZ, "File not found !"); + return; + } + + lines_count = 0; + while(rb->read_line(file, cur_line, MAX_CHARS_PER_LINE) && + lines_count < MAX_LINES) + { + lines_count++; + /* TODO: Check line length and split if too long + * rather than creating newlines */ + rb->strcpy(lines[lines_count - 1], cur_line); + } +} + +void save_file(bool new_file) +{ + if (!file_loaded || new_file) { + rb->strcpy(fname, "/default.txt"); + rb->splash(HZ, + "Filename not set. \ + Please indicate where to save the file (ex : \"/text.txt\""); + get_user_entry(fname, MAX_CHARS_PER_LINE); + } + + output_file = rb->open(fname, O_CREAT|O_TRUNC|O_WRONLY); + if (output_file == 0) { + rb->splash(HZ, "Error !"); + } + + int saved_line_id = 0; + for(saved_line_id = 0; saved_line_id < lines_count; saved_line_id++) { + rb->fdprintf(output_file, "%s%s", lines[saved_line_id], "\n"); + } + + rb->close(output_file); + rb->splash(HZ, "File saved !"); +} + +void load_menu(int menu_id) +{ + int menu_item = 0; + + if (menu_id == EXIT_MENU) { + MENUITEM_STRINGLIST(rockedit_menu, + " - Rockedit menu - ", + NULL, + "Open file", + "Exit menu", + "Exit rockedit", + "Save current file", + "Save current file as...", + "Save changes and exit rockedit"); + menu_item = rb->do_menu(&rockedit_menu, &menu_item, NULL, 0); + + switch(menu_item) { + case 0: + open_file(true); + break; + case 1: + break; + case 2: + exit_editor = true; + break; + case 3: + save_file(false); + break; + case 4: + save_file(true); + break; + case 5: + save_file(false); + exit_editor = true; + break; + case MENU_ATTACHED_USB: + usb = true; + break; + } + } + if (menu_id == TEXT_MENU) { + MENUITEM_STRINGLIST(text_menu, + " - Edition menu - ", + NULL, + "Exit menu", + "Remove line", + "Insert line before", + "Insert line after"); + menu_item = rb->do_menu(&text_menu, &menu_item, NULL, 0); + + switch(menu_item) { + case 0: + break; + case 1: + remove_line(cur_line_id); + break; + case 2: + insert_line(cur_line_id); + break; + case 3: + insert_line(cur_line_id + 1); + break; + case MENU_ATTACHED_USB: + usb = true; + break; + } + } +} + +enum plugin_status plugin_start +(const struct plugin_api* api, const void* parameter) { + rb = api; + (void)(parameter); + + init(); + int button; + line_height = rb->font_get(FONT_UI)->height; + nb_lines_shown = (LCD_HEIGHT - TOP_MARGIN) / + (line_height + line_spacing); + + if (parameter != 0) { + rb->strcpy(fname, parameter); + open_file(false); + } + + while(!exit_editor && !usb) { + if (!refresh_editor_screen) goto jump_refresh; + + rb->lcd_clear_display(); + int min_print_i = 0; + if (lines_count > nb_lines_shown) { + if (cur_line_id < lines_count - nb_lines_shown) + min_print_i = cur_line_id; + else + min_print_i = lines_count - nb_lines_shown; + } + int print_i; + int cur_vpos = TOP_MARGIN; + int pointer_w, number_w, line_init_w; + rb->lcd_getstringsize(">", &pointer_w, NULL); + + char number[5 + EOS]; + + for(print_i = min_print_i; print_i < lines_count; print_i++) { + rb->snprintf(number, 5 + EOS, "%d.", print_i + 1); + rb->lcd_getstringsize(number, &number_w, NULL); + + line_init_w = number_w > pointer_w ? number_w : pointer_w; + + rb->lcd_putsxy(0, cur_vpos, + (print_i == cur_line_id) ? ">" : number); + + cur_vpos += draw_string(lines[print_i], + rb->strlen(lines[print_i]), line_init_w, + cur_vpos, line_height + line_spacing, + LCD_WIDTH - line_init_w); + + if (cur_vpos > LCD_HEIGHT) break; + } + rb->lcd_update(); + + jump_refresh: + refresh_editor_screen = true; + + button = pluginlib_getaction(rb, TIMEOUT_BLOCK, plugin_contexts, 2); + switch(button) { + case EXIT: + load_menu(EXIT_MENU); + break; + + case MENU: + load_menu(TEXT_MENU); + break; + + case ACCEPT: + save_file(false); + exit_editor = true; + break; + + case SELECT: + get_user_entry(lines[cur_line_id], MAX_CHARS_PER_LINE); + break; + + case UP: + case UP_REPEAT: + cur_line_id--; + if (cur_line_id == -1) cur_line_id = lines_count - 1; + break; + + case DOWN: + case DOWN_REPEAT: + cur_line_id = (cur_line_id + 1) % (lines_count); + break; + + default: + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) + usb = true; + break; + } + } + + return (usb) ? PLUGIN_USB_CONNECTED : PLUGIN_OK; +} Modification de propriétés sur apps/plugins/rockedit.c ___________________________________________________________________ Nom : svn:executable + * Index: apps/plugins/SOURCES =================================================================== --- apps/plugins/SOURCES (révision 17722) +++ apps/plugins/SOURCES (copie de travail) @@ -152,3 +152,5 @@ #endif /* m:robe 500 */ md5sum.c +typing.c +rockedit.c