sled

Једноставни уређивач текста
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување f91aa7860c8e97ff54b4336b382e331de53ff1e0
родитељ 0bfda2400f710f300c58bb24f2a9bd882f53e5e5
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Mon, 14 Feb 2022 20:25:00 +0100

Reorganize key handler; add insert (default) and command mode (inverse vi)

Signed-off-by: Страхиња Радић <contact@strahinja.org>

Diffstat:
MREADME | 4++--
MTODO | 2+-
Mconfig.def.h | 118++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mconfig.h | 118++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mdraw.c | 41+++++++++++++++++++++++++++++------------
Mdraw.h | 8+++++++-
Msled.c | 805+++++++++++++++++++++++++++++++++++--------------------------------------------
Mutil.h | 8++++++++
измењених датотека: 8, додавања: 624(+), брисања: 480(-)

diff --git a/README b/README @@ -14,13 +14,13 @@ A: A text editor's purpose should be to *edit* text files. For just *viewing* Of course, one could argue that screen editors like vi already double as text file viewers, but I think of displaying the contents of a writable file - as separate from displaying the contents of a non-writable file. + as being separate from displaying the contents of a non-writable file. If this bothers you, you can use something like the included shell script sledv. Q: Is editing multiple files planned? -A: No, this is by design. You can edit multiple files at the same time by +A: No, and this is by design. You can edit multiple files at the same time by running multiple instances of sled. You can arrange them on screen through a terminal multiplexer like GNU screen(1) or tmux(1), or just open several terminal emulators like st(1) and have your window manager arrange their diff --git a/TODO b/TODO @@ -3,7 +3,7 @@ [~] Create a working base by migration from poe - [ ] Implement "inverse vi mode" + [x] Implement "inverse vi mode" Legend diff --git a/config.def.h b/config.def.h @@ -13,26 +13,130 @@ #define TITLE_FG TB_BLACK #define TITLE_BG TB_WHITE +int cancel_close(struct DrawState* state); +int enter_command_mode(struct DrawState* state); +int enter_insert_mode(struct DrawState* state); +int erase_backwards(struct DrawState* state); +int erase_forward(struct DrawState* state); +int erase_to_end(struct DrawState* state); +int erase_to_start(struct DrawState* state); +int exit_program(struct DrawState* state); +int insert_space(struct DrawState* state); +int insert_tab(struct DrawState* state); +int move_bottom(struct DrawState* state); +int move_down(struct DrawState* state); +int move_end(struct DrawState* state); +int move_left(struct DrawState* state); +int move_next_word(struct DrawState* state); +int move_page_down(struct DrawState* state); +int move_page_up(struct DrawState* state); +int move_prev_word(struct DrawState* state); +int move_right(struct DrawState* state); +int move_start(struct DrawState* state); +int move_top(struct DrawState* state); +int move_up(struct DrawState* state); +int show_help(struct DrawState* state); + +/* Always processed */ +static const struct Key top_level_keys[] = { + { .ev = { .key = TB_KEY_ESC }, .callback = cancel_close }, + { .ev = { .key = TB_KEY_CTRL_Q }, + .callback = exit_program } +}; + +/* Only when no dialog/prompt open */ +static const struct Key global_keys[] = { + { .ev = { .key = TB_KEY_ARROW_DOWN }, + .callback = move_down }, + { .ev = { .key = TB_KEY_ARROW_UP }, + .callback = move_up }, + { .ev = { .key = TB_KEY_ARROW_LEFT }, + .callback = move_left }, + { .ev = { .key = TB_KEY_ARROW_RIGHT }, + .callback = move_right }, + { .ev = { .key = TB_KEY_BACKSPACE }, + .callback = erase_backwards }, + { .ev = { .key = TB_KEY_BACKSPACE2 }, + .callback = erase_backwards }, + { .ev = { .key = TB_KEY_CTRL_A }, + .callback = move_start }, + { .ev = { .key = TB_KEY_CTRL_B }, + .callback = move_page_up }, + { .ev = { .key = TB_KEY_CTRL_E }, + .callback = move_end }, + { .ev = { .key = TB_KEY_CTRL_F }, + .callback = move_page_down }, + { .ev = { .key = TB_KEY_CTRL_K }, + .callback = erase_to_end }, + { .ev = { .key = TB_KEY_CTRL_R }, + .callback = move_prev_word }, + { .ev = { .key = TB_KEY_CTRL_T }, + .callback = move_next_word }, + { .ev = { .key = TB_KEY_CTRL_U }, + .callback = erase_to_start }, + { .ev = { .key = TB_KEY_DELETE }, + .callback = erase_forward }, + { .ev = { .key = TB_KEY_F1 }, .callback = show_help }, + { .ev = { .key = TB_KEY_END }, .callback = move_end }, + { .ev = { .key = TB_KEY_HOME }, .callback = move_start }, + { .ev = { .key = TB_KEY_PGDN }, .callback = move_page_down }, + { .ev = { .key = TB_KEY_PGUP }, .callback = move_page_up } +}; + +/* Only when in insert mode */ +static const struct Key insert_keys[] = { + { .ev = { .key = TB_KEY_CTRL_C }, + .callback = enter_command_mode }, + { .ev = { .key = TB_KEY_SPACE }, + .callback = insert_space }, + { .ev = { .key = TB_KEY_TAB }, + .callback = insert_tab } +}; + +/* Only when in command mode */ +static const struct Key command_keys[] = { + { .ev = { .key = TB_KEY_CTRL_C }, + .callback = enter_insert_mode }, + { .ev = { .ch = '0' }, .callback = move_start }, + { .ev = { .ch = '$' }, .callback = move_end }, + { .ev = { .ch = 'g' }, .callback = move_top }, + { .ev = { .ch = 'h' }, .callback = move_left }, + { .ev = { .ch = 'j' }, .callback = move_down }, + { .ev = { .ch = 'k' }, .callback = move_up }, + { .ev = { .ch = 'l' }, .callback = move_right }, + { .ev = { .ch = 'q' }, .callback = exit_program }, + { .ev = { .ch = 'w' }, .callback = move_next_word }, + { .ev = { .ch = 'G' }, .callback = move_bottom }, + { .ev = { .ch = 'H' }, .callback = show_help }, + { .ev = { .ch = 'W' }, .callback = move_prev_word } +}; + void format_filename(char* result, size_t result_size, const char* format, const struct DrawState* state); - +void format_mode(char* result, size_t result_size, const char* format, + const struct DrawState* state); void format_position(char* result, size_t result_size, const char* format, const struct DrawState* state); static const struct StatusSegment status_segments[] = { {.format = "%s%s", .callback = format_filename, - .fg = TB_BLACK, - .bg = TB_WHITE, + .fg = TB_WHITE, + .bg = TB_DEFAULT, .alignment = LEFT}, + { .format = "%s", + .callback = format_mode, + .fg = TB_YELLOW | TB_BOLD, + .bg = TB_DEFAULT, + .alignment = CENTER}, {.format = "%ld:%ld (%ld%%)", .callback = format_position, .fg = TB_WHITE | TB_BOLD, - .bg = TB_BLUE, + .bg = TB_DEFAULT, .alignment = CENTER}, - {.format = "H=HELP, q=QUIT", + {.format = "F1=HELP, C-Q=QUIT", .callback = NULL, - .fg = TB_BLACK, - .bg = TB_WHITE, + .fg = TB_WHITE, + .bg = TB_DEFAULT, .alignment = RIGHT}, }; diff --git a/config.h b/config.h @@ -13,26 +13,130 @@ #define TITLE_FG TB_BLACK #define TITLE_BG TB_WHITE +int cancel_close(struct DrawState* state); +int enter_command_mode(struct DrawState* state); +int enter_insert_mode(struct DrawState* state); +int erase_backwards(struct DrawState* state); +int erase_forward(struct DrawState* state); +int erase_to_end(struct DrawState* state); +int erase_to_start(struct DrawState* state); +int exit_program(struct DrawState* state); +int insert_space(struct DrawState* state); +int insert_tab(struct DrawState* state); +int move_bottom(struct DrawState* state); +int move_down(struct DrawState* state); +int move_end(struct DrawState* state); +int move_left(struct DrawState* state); +int move_next_word(struct DrawState* state); +int move_page_down(struct DrawState* state); +int move_page_up(struct DrawState* state); +int move_prev_word(struct DrawState* state); +int move_right(struct DrawState* state); +int move_start(struct DrawState* state); +int move_top(struct DrawState* state); +int move_up(struct DrawState* state); +int show_help(struct DrawState* state); + +/* Always processed */ +static const struct Key top_level_keys[] = { + { .ev = { .key = TB_KEY_ESC }, .callback = cancel_close }, + { .ev = { .key = TB_KEY_CTRL_Q }, + .callback = exit_program } +}; + +/* Only when no dialog/prompt open */ +static const struct Key global_keys[] = { + { .ev = { .key = TB_KEY_ARROW_DOWN }, + .callback = move_down }, + { .ev = { .key = TB_KEY_ARROW_UP }, + .callback = move_up }, + { .ev = { .key = TB_KEY_ARROW_LEFT }, + .callback = move_left }, + { .ev = { .key = TB_KEY_ARROW_RIGHT }, + .callback = move_right }, + { .ev = { .key = TB_KEY_BACKSPACE }, + .callback = erase_backwards }, + { .ev = { .key = TB_KEY_BACKSPACE2 }, + .callback = erase_backwards }, + { .ev = { .key = TB_KEY_CTRL_A }, + .callback = move_start }, + { .ev = { .key = TB_KEY_CTRL_B }, + .callback = move_page_up }, + { .ev = { .key = TB_KEY_CTRL_E }, + .callback = move_end }, + { .ev = { .key = TB_KEY_CTRL_F }, + .callback = move_page_down }, + { .ev = { .key = TB_KEY_CTRL_K }, + .callback = erase_to_end }, + { .ev = { .key = TB_KEY_CTRL_R }, + .callback = move_prev_word }, + { .ev = { .key = TB_KEY_CTRL_T }, + .callback = move_next_word }, + { .ev = { .key = TB_KEY_CTRL_U }, + .callback = erase_to_start }, + { .ev = { .key = TB_KEY_DELETE }, + .callback = erase_forward }, + { .ev = { .key = TB_KEY_F1 }, .callback = show_help }, + { .ev = { .key = TB_KEY_END }, .callback = move_end }, + { .ev = { .key = TB_KEY_HOME }, .callback = move_start }, + { .ev = { .key = TB_KEY_PGDN }, .callback = move_page_down }, + { .ev = { .key = TB_KEY_PGUP }, .callback = move_page_up } +}; + +/* Only when in insert mode */ +static const struct Key insert_keys[] = { + { .ev = { .key = TB_KEY_CTRL_C }, + .callback = enter_command_mode }, + { .ev = { .key = TB_KEY_SPACE }, + .callback = insert_space }, + { .ev = { .key = TB_KEY_TAB }, + .callback = insert_tab } +}; + +/* Only when in command mode */ +static const struct Key command_keys[] = { + { .ev = { .key = TB_KEY_CTRL_C }, + .callback = enter_insert_mode }, + { .ev = { .ch = '0' }, .callback = move_start }, + { .ev = { .ch = '$' }, .callback = move_end }, + { .ev = { .ch = 'g' }, .callback = move_top }, + { .ev = { .ch = 'h' }, .callback = move_left }, + { .ev = { .ch = 'j' }, .callback = move_down }, + { .ev = { .ch = 'k' }, .callback = move_up }, + { .ev = { .ch = 'l' }, .callback = move_right }, + { .ev = { .ch = 'q' }, .callback = exit_program }, + { .ev = { .ch = 'w' }, .callback = move_next_word }, + { .ev = { .ch = 'G' }, .callback = move_bottom }, + { .ev = { .ch = 'H' }, .callback = show_help }, + { .ev = { .ch = 'W' }, .callback = move_prev_word } +}; + void format_filename(char* result, size_t result_size, const char* format, const struct DrawState* state); - +void format_mode(char* result, size_t result_size, const char* format, + const struct DrawState* state); void format_position(char* result, size_t result_size, const char* format, const struct DrawState* state); static const struct StatusSegment status_segments[] = { {.format = "%s%s", .callback = format_filename, - .fg = TB_BLACK, - .bg = TB_WHITE, + .fg = TB_WHITE, + .bg = TB_DEFAULT, .alignment = LEFT}, + { .format = "%s", + .callback = format_mode, + .fg = TB_YELLOW | TB_BOLD, + .bg = TB_DEFAULT, + .alignment = CENTER}, {.format = "%ld:%ld (%ld%%)", .callback = format_position, .fg = TB_WHITE | TB_BOLD, - .bg = TB_BLUE, + .bg = TB_DEFAULT, .alignment = CENTER}, - {.format = "H=HELP, q=QUIT", + {.format = "F1=HELP, C-Q=QUIT", .callback = NULL, - .fg = TB_BLACK, - .bg = TB_WHITE, + .fg = TB_WHITE, + .bg = TB_DEFAULT, .alignment = RIGHT}, }; diff --git a/draw.c b/draw.c @@ -12,24 +12,40 @@ #include "version.h" /* clang-format off */ -static const char* help_title = "Main view help"; +static const char* help_title = "sled help"; static const char* help[] = { - "Esc - Close dialog, clear error", - "H or F1 - Show this screen", - "q or C-Q - Quit", + "Esc - Close dialog, clear error", + "F1 - Show this screen", + "C-C - Switch command <-> insert mode", + "Home or C-A - Go to start of line", + "PgUp or C-B - Go to previous page", + "PgDown or C-F - Go to next page", + "End or C-E - Go to end of line", + "C-Q - Quit", + "", + "Arrow keys work both in insert and command mode.", + "Some vi-like keys work in command mode.", + "", "See the manual (man sled) for full help."}; -const char* errors[] = { - [ERR_UNKNOWN_KEY] = "Unknown key (press H for help)", - [ERR_EXIT_KEY] = "Press q or C-Q to quit", - [ERR_DLG_OPEN] = "Press Esc to close dialog or q/C-Q to quit", +const char* mode_labels[] = { + [UNKNOWN_MODE] = "UNKNOWN", + [COMMAND_MODE] = "COMMAND", + [HELP_MODE] = "HELP", + [INSERT_MODE] = "INSERT", + [PROMPT_MODE] = "PROMPT" +}; +const char* errors[] = { + [ERR_UNKNOWN_KEY] = "Unknown key (press F1 for help)", + [ERR_EXIT_KEY] = "Press C-Q to quit", + [ERR_DLG_OPEN] = "Press Esc to close dialog or C-Q to quit", [ERR_DLG_OPEN_QUIT] = "Press Esc to close dialog", [ERR_CANT_ALLOC] = "Memory allocation failed", [ERR_CANT_SAVE] = "File is not writeable", [ERR_CANT_MOVE] = "Cannot move temporary file"}; -const char* program_name = "sled"; -const char* prompt_dirty = "File changed, save [y/n/ESC]?"; +const char* program_name = "sled"; +const char* prompt_dirty = "File changed, save [y/n/ESC]?"; const char* prompt_cancel_dirty = "Entry changed, save [y/n/ESC]?"; -const char* prompt_overwrite = "Not empty, overwrite? [y/n/ESC]?"; +const char* prompt_overwrite = "Not empty, overwrite? [y/n/ESC]?"; /* clang-format on */ void @@ -77,6 +93,7 @@ init_drawstate(struct DrawState* state, char* error, char* filename, state->running = 1; state->dirty = 0; state->in_prompt = 0; + state->in_command_mode = 0; state->show_help = 0; } @@ -503,7 +520,7 @@ draw_status(const struct DrawState* state) return NULL; char buf[MAXBUFLINE]; - int seg_size = state->maxx / 3; + int seg_size = state->maxx / LEN(status_segments); int current_start = 0; const struct StatusSegment* pseg = status_segments; diff --git a/draw.h b/draw.h @@ -10,7 +10,7 @@ #define BORDER_VER ((uint32_t)L'\u2502') #define BORDER_HOR ((uint32_t)L'\u2500') -#define HELP_WIDTH 50 +#define HELP_WIDTH 60 #define HELP_HEIGHT 21 #define TAB_SIZE 8 @@ -43,11 +43,17 @@ struct DrawState { int running; int dirty; int in_prompt; + int in_command_mode; int show_help; int maxx; int maxy; }; +struct Key { + struct tb_event ev; + int (*callback)(struct DrawState*); +}; + struct StatusSegment { const char* format; void (*callback)(char*, size_t, const char*, const struct DrawState*); diff --git a/sled.c b/sled.c @@ -36,6 +36,7 @@ enum { }; extern const char* errors[]; +extern const char* mode_labels[]; extern const char* prompt_dirty; extern const char* prompt_cancel_dirty; extern const char* prompt_overwrite; @@ -58,32 +59,6 @@ struct BufferLine* load_info(struct DrawState* state); struct DrawState* load_msgstr(struct DrawState* state); int load_file(struct DrawState* dstate, long* lineno, long* col); int save_file(struct DrawState* state); -void save_msgstr(struct DrawState* state); -void cancel_msgstr(struct DrawState* state); -void goto_msgid(struct DrawState* state, const size_t msgid_number); -void show_edit(struct DrawState* state); -void move_up(struct DrawState* state); -void move_down(struct DrawState* state); -void move_start(struct DrawState* state); -void move_left(struct DrawState* state); -void move_right(struct DrawState* state); -void move_prev_word(struct DrawState* state); -void move_next_word(struct DrawState* state); -void erase_prev_word(struct DrawState* state); -void move_end(struct DrawState* state); -void move_list_up(struct DrawState* state); -void move_list_down(struct DrawState* state); -void move_list_start(struct DrawState* state); -void move_list_end(struct DrawState* state); -void move_list_page_up(struct DrawState* state); -void move_list_page_down(struct DrawState* state); -void next_plural(struct DrawState* state); -void prev_plural(struct DrawState* state); -void insert_char(struct DrawState* state, uint32_t ch); -void erase_backwards(struct DrawState* state); -void erase_forward(struct DrawState* state); -void erase_to_start(struct DrawState* state); -void erase_to_end(struct DrawState* state); struct DrawState* insert_line(struct DrawState* state); struct DrawState* join_lines(struct DrawState* state, size_t first_line, int forward); @@ -129,6 +104,25 @@ format_filename(char* result, size_t result_size, const char* format, } void +format_mode(char* result, size_t result_size, const char* format, + const struct DrawState* state) +{ + const char* mode_label = mode_labels[UNKNOWN_MODE]; + if (!result) + return; + if (state->show_help) + mode_label = mode_labels[HELP_MODE]; + else if (state->in_prompt) + mode_label = mode_labels[PROMPT_MODE]; + else if (state->in_command_mode) + mode_label = mode_labels[COMMAND_MODE]; + else + mode_label = mode_labels[INSERT_MODE]; + + snprintf(result, result_size, format, mode_label); +} + +void format_position(char* result, size_t result_size, const char* format, const struct DrawState* state) { @@ -178,10 +172,11 @@ cancel_callback(struct DrawState* state, struct tb_event* ev) if (!state) return; - if (ev->ch == 'y') + /*if (ev->ch == 'y') save_msgstr(state); else cancel_msgstr(state); + */ state->prompt_callback = NULL; } @@ -387,49 +382,6 @@ save_msgstr(struct DrawState* state) */ } -void -cancel_msgstr(struct DrawState* state) -{ - if (!state->in_prompt && state->dirty) - { - state->in_prompt = 1; - strcpy(state->prompt, prompt_cancel_dirty); - state->prompt_callback = cancel_callback; - return; - } - - /* - state->show_edit = 0; - state->edit_info_focused = 0; - - for (size_t i = 0; i < state->input_rows_count; i++) - free_bufferline(state->input_buffer + i); - free(state->input_buffer); - state->input_buffer = NULL; - state->input_rows_count = 0; - if (state->info_buffer) - for (size_t i = 0; i < state->info_rows_count; i++) - free_bufferline(state->info_buffer + i); - free(state->info_buffer); - state->info_buffer = NULL; - state->info_rows_count = 0; - state->dirty = state->dirty_before_edit; - state->fuzzy_count = state->fuzzy_before_edit; - switch (state->input_changed_fuzzy) - { - case SET: - state->entries[state->msgid_number - 1].flags &= ~FL_FUZZY; - break; - case CLEARED: - state->entries[state->msgid_number - 1].flags |= FL_FUZZY; - break; - default:; - } - state->input_changed_fuzzy = UNCHANGED; -*/ - tb_set_cursor(TB_HIDE_CURSOR, TB_HIDE_CURSOR); -} - struct DrawState* paste_paste_buffer_to_input(struct DrawState* state) { @@ -520,7 +472,7 @@ paste_paste_buffer_to_input(struct DrawState* state) return state; } -void +int move_up(struct DrawState* state) { if (state->row > 0) @@ -544,9 +496,10 @@ move_up(struct DrawState* state) = state->display_column - state->maxx + 1; state->display_column = display_length(state, state->buffer[state->row].text, state->column); + return 0; } -void +int move_down(struct DrawState* state) { if (state->row < state->rows_count - 1) @@ -570,67 +523,65 @@ move_down(struct DrawState* state) = state->display_column - state->maxx + 1; state->display_column = display_length(state, state->buffer[state->row].text, state->column); + return 0; } -void +int move_top(struct DrawState* state) { state->row = 0; state->first_shown_row = 0; - - move_start(state); + return 0; } -void +int move_start(struct DrawState* state) { state->column = 0; state->display_column = 0; state->first_shown_column = 0; + return 0; } -void +int move_left(struct DrawState* state) { if (state->column == 0) - return; + return 0; - if (state->buffer[state->row].text[state->column - 1] - == '\t') + if (state->buffer[state->row].text[state->column - 1] == '\t') state->display_column = display_length(state, - state->buffer[state->row].text, - state->column - 1); + state->buffer[state->row].text, state->column - 1); else state->display_column--; state->column--; if (state->display_column < state->first_shown_column) state->first_shown_column = state->display_column; + return 0; } -void +int move_right(struct DrawState* state) { size_t len = state->buffer[state->row].length; if (state->column == len) - return; + return 0; - if (state->buffer[state->row].text[state->column] - == '\t') + if (state->buffer[state->row].text[state->column] == '\t') state->display_column = display_length(state, - state->buffer[state->row].text, - state->column + 1); + state->buffer[state->row].text, state->column + 1); else state->display_column++; state->column++; - if (state->display_column - state->first_shown_column - > state->maxx - 1) + if (state->display_column - state->first_shown_column > state->maxx - 1) state->first_shown_column = state->display_column - state->maxx + 1; + return 0; } -void +int move_prev_word(struct DrawState* state) { int i = state->column; @@ -644,15 +595,15 @@ move_prev_word(struct DrawState* state) && !u32_is_word_boundary(pline[i - 1], 0)) i--; - state->column = i; + state->column = i; state->display_column = display_length(state, - state->buffer[state->row].text, - state->column); + state->buffer[state->row].text, state->column); if (state->display_column < state->first_shown_column) state->first_shown_column = state->display_column; + return 0; } -void +int move_next_word(struct DrawState* state) { int i = state->column; @@ -663,14 +614,13 @@ move_next_word(struct DrawState* state) i++; while (i < len && u32_is_word_boundary(pline[i], 0)) i++; - state->column = i; + state->column = i; state->display_column = display_length(state, - state->buffer[state->row].text, - state->column); - if (state->display_column - state->first_shown_column - > state->maxx - 1) + state->buffer[state->row].text, state->column); + if (state->display_column - state->first_shown_column > state->maxx - 1) state->first_shown_column = state->display_column - state->maxx + 1; + return 0; } void @@ -712,17 +662,16 @@ erase_prev_word(struct DrawState* state) */ } -void +int move_bottom(struct DrawState* state) { state->row = state->rows_count - 1; state->first_shown_row = state->row > state->maxy ? state->row - state->maxy + 2 : 0; - - move_start(state); + return 0; } -void +int move_end(struct DrawState* state) { size_t len = state->buffer[state->row].length; @@ -736,60 +685,10 @@ move_end(struct DrawState* state) state->first_shown_column = dlen - state->maxx + 4; else state->first_shown_column = 0; + return 0; } -void -move_list_up(struct DrawState* state) -{ - /* - if (state->msgid_number > 1) - { - state->error[0] = 0; - state->msgid_number--; - if (state->msgid_number < state->first_shown_msgid) - state->first_shown_msgid--; - } - */ -} - -void -move_list_down(struct DrawState* state) -{ - /* - if (state->msgid_number < state->msgid_count) - { - state->error[0] = 0; - state->msgid_number++; - if (state->msgid_number - state->first_shown_msgid - > state->maxy - 2) - state->first_shown_msgid++; - } - */ -} - -void -move_list_start(struct DrawState* state) -{ - /* - state->error[0] = 0; - state->msgid_number = state->msgid_count > 0 ? 1 : 0; - state->first_shown_msgid = state->msgid_number; - */ -} - -void -move_list_end(struct DrawState* state) -{ - /* - state->error[0] = 0; - state->msgid_number = state->msgid_count; - state->first_shown_msgid = state->msgid_count > 0 - ? MAX(1, state->msgid_number - (state->maxy - 2)) - : 0; - */ -} - -void +int move_page_up(struct DrawState* state) { *state->error = 0; @@ -801,9 +700,10 @@ move_page_up(struct DrawState* state) } else move_top(state); + return 0; } -void +int move_page_down(struct DrawState* state) { *state->error = 0; @@ -815,85 +715,67 @@ move_page_down(struct DrawState* state) } else move_bottom(state); + return 0; } -void -next_plural(struct DrawState* state) -{ - /* - if (state->edit_info_focused) - return; - struct PoEntry* current = &state->entries[state->msgid_number - 1]; - save_msgstr(state); - if (state->msgstr_index + 1 < current->msgstr_count) - state->msgstr_index++; - show_edit(state); - */ -} - -void -prev_plural(struct DrawState* state) -{ - /* - if (state->edit_info_focused) - return; - save_msgstr(state); - if (state->msgstr_index > 0) - state->msgstr_index--; - show_edit(state); - */ -} - -void +int insert_char(struct DrawState* state, uint32_t ch) { - /* - if (state->edit_info_focused) - return; - struct BufferLine* buffer_line = &state->input_buffer[state->input_row]; + struct BufferLine* buffer_line = &state->buffer[state->row]; size_t len = buffer_line->length; - if (state->input_column < MAXBUFLINE - 1) + if (state->column < MAXBUFLINE - 1) { size_t i = len + 1; - int add_null = buffer_line->text[state->input_column] == 0; - while (i < MAXBUFLINE - 2 && i > state->input_column) + int add_null = buffer_line->text[state->column] == 0; + while (i < MAXBUFLINE - 2 && i > state->column) { buffer_line->text[i] = buffer_line->text[i - 1]; i--; } - buffer_line->text[state->input_column++] = ch; + buffer_line->text[state->column++] = ch; buffer_line->length++; - if (state->input_column == MAXBUFLINE) - state->input_column = MAXBUFLINE - 1; - state->input_display_column = display_length(state, - state->input_buffer[state->input_row].text, - state->input_column); - if (state->input_display_column - - state->input_first_shown_column > state->edit_width - 4) + if (state->column == MAXBUFLINE) + state->column = MAXBUFLINE - 1; + state->display_column = display_length(state, + state->buffer[state->row].text, + state->column); + if (state->display_column - + state->first_shown_column > state->maxx - 1) { - state->input_first_shown_column - = state->input_display_column - - state->edit_width + 4; - state->input_display_column = display_length(state, - state->input_buffer[state->input_row].text, - state->input_column); + state->first_shown_column + = state->display_column + - state->maxx + 1; + state->display_column = display_length(state, + state->buffer[state->row].text, + state->column); } if (add_null) - buffer_line->text[state->input_column] = 0; + buffer_line->text[state->column] = 0; } state->dirty = 1; - */ + return 0; } -void +int +insert_space(struct DrawState* state) +{ + return insert_char(state, ' '); +} + +int +insert_tab(struct DrawState* state) +{ + return insert_char(state, '\t'); +} + +int erase_backwards(struct DrawState* state) { - /* - struct BufferLine* buffer_line = &state->input_buffer[state->input_row]; + struct BufferLine* buffer_line = &state->buffer[state->row]; - if (state->input_column > 0) + if (state->column > 0) { - size_t i = state->input_column; + size_t i = state->column; do { buffer_line->text[i - 1] = buffer_line->text[i]; @@ -901,43 +783,42 @@ erase_backwards(struct DrawState* state) } while (buffer_line->text[i]); buffer_line->text[i - 1] = 0; buffer_line->length--; - state->input_column--; - if (state->input_first_shown_column > 0) + state->column--; + if (state->first_shown_column > 0) { const struct BufferLine* current_line - = &state->input_buffer[state->input_row]; + = &state->buffer[state->row]; if (display_length(state, current_line->text, current_line->length) - >= state->edit_width - 4) - state->input_first_shown_column--; + >= state->maxx - 1) + state->first_shown_column--; } - state->input_display_column = display_length(state, - state->input_buffer[state->input_row].text, - state->input_column); - if (state->input_display_column - < state->input_first_shown_column) - state->input_first_shown_column - = state->input_display_column; + state->display_column = display_length(state, + state->buffer[state->row].text, + state->column); + if (state->display_column + < state->first_shown_column) + state->first_shown_column + = state->display_column; state->dirty = 1; } - else if (state->input_row > 0) + else if (state->row > 0) { move_up(state); move_end(state); erase_forward(state); } - */ + return 0; } -void +int erase_forward(struct DrawState* state) { - /* - struct BufferLine* buffer_line = &state->input_buffer[state->input_row]; + struct BufferLine* buffer_line = &state->buffer[state->row]; - if (buffer_line->text[state->input_column]) + if (buffer_line->text[state->column]) { - size_t i = state->input_column; + size_t i = state->column; while (i < MAXBUFLINE - 2 && buffer_line->text[i + 1]) { buffer_line->text[i] = buffer_line->text[i + 1]; @@ -947,99 +828,85 @@ erase_forward(struct DrawState* state) buffer_line->length--; state->dirty = 1; } - else if (state->input_row < state->input_rows_count - 1) + else if (state->row < state->rows_count - 1) { - if (!join_lines(state, state->input_row, 1)) + if (!join_lines(state, state->row, 1)) { strcpy(state->error, errors[ERR_CANT_ALLOC]); state->running = 0; } } - */ + return 0; } -void +int erase_to_start(struct DrawState* state) { - /* - if (state->edit_info_focused) - { - strcpy(state->error, errors[ERR_DLG_OPEN_QUIT]); - return; - } - - size_t i = state->input_column; - struct BufferLine* buffer_line = &state->input_buffer[state->input_row]; + size_t i = state->column; + struct BufferLine* buffer_line = &state->buffer[state->row]; size_t len = buffer_line->length; do { - buffer_line->text[i - state->input_column] + buffer_line->text[i - state->column] = buffer_line->text[i]; i++; } while (buffer_line->text[i]); - buffer_line->text[len - state->input_column] = 0; - buffer_line->length = len - state->input_column; + buffer_line->text[len - state->column] = 0; + buffer_line->length = len - state->column; move_start(state); state->dirty = 1; - */ + return 0; } -void +int erase_to_end(struct DrawState* state) { - /* - if (state->edit_info_focused) - return; - struct BufferLine* buffer_line = &state->input_buffer[state->input_row]; - buffer_line->text[state->input_column] = 0; - buffer_line->length = state->input_column; + struct BufferLine* buffer_line = &state->buffer[state->row]; + buffer_line->text[state->column] = 0; + buffer_line->length = state->column; move_end(state); state->dirty = 1; - */ + return 0; } struct DrawState* insert_line(struct DrawState* state) { - /* - if (state->edit_info_focused) - return state; - state->input_rows_count++; - struct BufferLine* newbuf = realloc(state->input_buffer, - state->input_rows_count * sizeof(struct BufferLine)); + state->rows_count++; + struct BufferLine* newbuf = realloc(state->buffer, + state->rows_count * sizeof(struct BufferLine)); if (!newbuf) return NULL; - state->input_buffer = newbuf; - init_bufferline(&state->input_buffer[state->input_rows_count - 1]); + state->buffer = newbuf; + init_bufferline(&state->buffer[state->rows_count - 1]); uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); if (!newtext) return NULL; - state->input_buffer[state->input_rows_count - 1].text = newtext; - for (size_t i = state->input_rows_count - 1; i > state->input_row + 1; + state->buffer[state->rows_count - 1].text = newtext; + for (size_t i = state->rows_count - 1; i > state->row + 1; i--) { - u32_strncpy(state->input_buffer[i].text, - state->input_buffer[i - 1].text, MAXBUFLINE); - state->input_buffer[i].length - = state->input_buffer[i - 1].length; + u32_strncpy(state->buffer[i].text, + state->buffer[i - 1].text, MAXBUFLINE); + state->buffer[i].length + = state->buffer[i - 1].length; } - u32_strncpy(state->input_buffer[state->input_row + 1].text, - state->input_buffer[state->input_row].text + - state->input_column, MAXBUFLINE); state->input_buffer[state->input_row + - 1].length = u32_strlen(state->input_buffer[state->input_row + 1].text); - state->input_buffer[state->input_row].text[state->input_column] = 0; - state->input_buffer[state->input_row].length - = u32_strlen(state->input_buffer[state->input_row].text); - - state->input_row++; - if (state->input_row - > state->input_first_shown_row + (EDIT_HEIGHT - 3) / 2) - state->input_first_shown_row - = state->input_row - (EDIT_HEIGHT - 3) / 2; + u32_strncpy(state->buffer[state->row + 1].text, + state->buffer[state->row].text + + state->column, MAXBUFLINE); state->buffer[state->row + + 1].length = u32_strlen(state->buffer[state->row + 1].text); + state->buffer[state->row].text[state->column] = 0; + state->buffer[state->row].length + = u32_strlen(state->buffer[state->row].text); + + state->row++; + if (state->row + > state->first_shown_row + (state->maxy - 1) / 2) // TODO + state->first_shown_row + = state->row - (state->maxy - 1) / 2; move_start(state); state->dirty = 1; -*/ return state; } @@ -1049,40 +916,98 @@ join_lines(struct DrawState* state, size_t first_line, int forward) if (!state) return NULL; - /* - state->input_column = state->input_buffer[first_line].length; - state->input_display_column = display_length(state, - state->input_buffer[state->input_row].text, - state->input_column); - u32_strncat(state->input_buffer[first_line].text, - state->input_buffer[first_line + 1].text, MAXBUFLINE); - state->input_buffer[first_line].length - = u32_strlen(state->input_buffer[first_line].text); + state->column = state->buffer[first_line].length; + state->display_column = display_length(state, + state->buffer[state->row].text, + state->column); + u32_strncat(state->buffer[first_line].text, + state->buffer[first_line + 1].text, MAXBUFLINE); + state->buffer[first_line].length + = u32_strlen(state->buffer[first_line].text); if (!forward) { - state->input_row--; - if (state->input_row < 0) - state->input_row = 0; + state->row--; + if (state->row < 0) + state->row = 0; } - for (size_t i = first_line + 1; i < state->input_rows_count - 1; i++) + for (size_t i = first_line + 1; i < state->rows_count - 1; i++) { - u32_strncpy(state->input_buffer[i].text, - state->input_buffer[i + 1].text, MAXBUFLINE); - state->input_buffer[i].length - = state->input_buffer[i + 1].length; + u32_strncpy(state->buffer[i].text, + state->buffer[i + 1].text, MAXBUFLINE); + state->buffer[i].length + = state->buffer[i + 1].length; } - free_bufferline(state->input_buffer + state->input_rows_count - 1); - state->input_rows_count--; - struct BufferLine* newbuf = realloc(state->input_buffer, - state->input_rows_count * sizeof(struct BufferLine)); + free_bufferline(state->buffer + state->rows_count - 1); + state->rows_count--; + struct BufferLine* newbuf = realloc(state->buffer, + state->rows_count * sizeof(struct BufferLine)); if (!newbuf) return NULL; - state->input_buffer = newbuf; + state->buffer = newbuf; state->dirty = 1; -*/ return state; } +int +cancel_close(struct DrawState* state) +{ + if (!state) + return 1; + + if (!*state->error && !state->in_prompt && !state->show_help) + strcpy(state->error, errors[ERR_EXIT_KEY]); + else + { + *state->error = 0; + *state->prompt = 0; + state->in_prompt = 0; + state->show_help = 0; + } + + return 0; +} + +int +enter_command_mode(struct DrawState* state) +{ + if (!state) + return 1; + state->in_command_mode = 1; + return 0; +} + +int +enter_insert_mode(struct DrawState* state) +{ + if (!state) + return 1; + state->in_command_mode = 0; + return 0; +} + +int +exit_program(struct DrawState* state) +{ + if (state->dirty) + { + state->in_prompt = 1; + strcpy(state->prompt, prompt_dirty); + state->prompt_callback = quit_callback; + } + else + state->running = 0; + *state->error = 0; + return 0; +} + +int +show_help(struct DrawState* state) +{ + state->show_help = 1; + *state->error = 0; + return 0; +} + void handle_editbox_key_event(struct tb_event* ev, struct DrawState* state) { @@ -1117,17 +1042,10 @@ handle_editbox_key_event(struct tb_event* ev, struct DrawState* state) move_right(state); else if (ev->key == TB_KEY_ARROW_UP) move_up(state); - else if ((ev->key == TB_KEY_BACKSPACE || ev->key == TB_KEY_BACKSPACE2) - /*&& !state->edit_info_focused*/) - erase_backwards(state); else if (ev->key == TB_KEY_CTRL_C) ; // yank_msgstr_to_paste_buffer(state); else if (ev->key == TB_KEY_CTRL_K) erase_to_end(state); - else if (ev->key == TB_KEY_CTRL_N) - next_plural(state); - else if (ev->key == TB_KEY_CTRL_P) - prev_plural(state); else if (ev->key == TB_KEY_CTRL_Q) strcpy(state->error, errors[ERR_DLG_OPEN_QUIT]); else if (ev->key == TB_KEY_CTRL_R) @@ -1144,8 +1062,6 @@ handle_editbox_key_event(struct tb_event* ev, struct DrawState* state) erase_prev_word(state); else if (ev->key == TB_KEY_CTRL_Z) ; // toggle_fuzzy(state); - else if (ev->key == TB_KEY_DELETE /* && !state->edit_info_focused*/) - erase_forward(state); else if (ev->key == TB_KEY_END || ev->key == TB_KEY_CTRL_E) move_end(state); else if (ev->key == TB_KEY_ENTER) @@ -1156,29 +1072,6 @@ handle_editbox_key_event(struct tb_event* ev, struct DrawState* state) state->running = 0; } } - else if (ev->key == TB_KEY_ESC) - cancel_msgstr(state); - else if (ev->key == TB_KEY_F5) - ; // copy_msgid_to_input(state); - else if (ev->key == TB_KEY_F6) - // state->edit_info_focused = !state->edit_info_focused; - ; - else if (ev->key == TB_KEY_F8) - ; // next_untranslated(state); - else if (ev->key == TB_KEY_F9) - ; // next_fuzzy(state); - else if (ev->key == TB_KEY_HOME || ev->key == TB_KEY_CTRL_A) - move_start(state); - else if (ev->key == TB_KEY_PGDN || ev->key == TB_KEY_CTRL_F) - ;//edit_page_down(state); - else if (ev->key == TB_KEY_PGUP || ev->key == TB_KEY_CTRL_B) - ;//edit_page_up(state); - else if (ev->key == TB_KEY_SPACE) - insert_char(state, ' '); - else if (ev->key == TB_KEY_TAB) - insert_char(state, '\t'); - else if (ev->ch != 0) - insert_char(state, ev->ch); } void @@ -1187,134 +1080,146 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) if (!state) return; - /* - if (state->show_edit) - handle_editbox_key_event(ev, state); - else if (state->show_search) - handle_search_key_event(ev, state); - else */ - - if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP - && ev->ch != 'j' && ev->ch != 'k') - state->saved_column = -1; - - if (ev->key == TB_KEY_ESC) - { - if (state->error[0]) - state->error[0] = 0; - else if (state->in_prompt) - { - state->in_prompt = 0; - state->prompt[0] = 0; - return; - } - else if (state->show_help) - state->show_help = 0; - else - strcpy(state->error, errors[ERR_EXIT_KEY]); - } - else if ((ev->ch == 'y' || ev->ch == 'n') && state->in_prompt) + if ((ev->ch == 'y' || ev->ch == 'n') && state->in_prompt) { if (state->prompt_callback) state->prompt_callback(state, ev); state->in_prompt = 0; *state->prompt = 0; + return; } - else if (state->in_prompt) + + /* Remember to also edit the following condition if you reassign motion + * keys */ + if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP + && !(state->in_command_mode && strchr("jkgG", ev->ch))) + state->saved_column = -1; + + for (int key = 0; key < LEN(top_level_keys); key++) + if ((top_level_keys[key].ev.key + && (top_level_keys[key].ev.key == ev->key)) + || (top_level_keys[key].ev.ch + && (top_level_keys[key].ev.ch == ev->ch))) + if (top_level_keys[key].callback + && !top_level_keys[key].callback(state)) + return; + + if (state->in_prompt) return; - else if (ev->ch == 'q' || ev->key == TB_KEY_CTRL_Q) + + if (state->show_help) { - if (state->dirty) - { - state->in_prompt = 1; - strcpy(state->prompt, prompt_dirty); - state->prompt_callback = quit_callback; - } - else - state->running = 0; - state->error[0] = 0; - } - else if (state->show_help) strcpy(state->error, errors[ERR_DLG_OPEN]); - else if (ev->key == TB_KEY_ARROW_DOWN || ev->ch == 'j') - move_down(state); - else if (ev->key == TB_KEY_ARROW_LEFT || ev->ch == 'h') - move_left(state); - else if (ev->key == TB_KEY_ARROW_RIGHT || ev->ch == 'l') - move_right(state); - else if (ev->key == TB_KEY_ARROW_UP || ev->ch == 'k') - move_up(state); - else if (ev->key == TB_KEY_CTRL_B || ev->key == TB_KEY_PGUP) - move_page_up(state); - else if (ev->key == TB_KEY_CTRL_F || ev->key == TB_KEY_PGDN) - move_page_down(state); - else if (ev->key == TB_KEY_CTRL_S || ev->ch == 'w') + return; + } + + for (int key = 0; key < LEN(global_keys); key++) + if ((global_keys[key].ev.key + && (global_keys[key].ev.key == ev->key)) + || (global_keys[key].ev.ch + && (global_keys[key].ev.ch == ev->ch))) + if (global_keys[key].callback + && !global_keys[key].callback(state)) + return; + + if (state->in_command_mode) { - if (!state->dirty) - return; - /* - int result = save_file(state->entries, state->real_msgid_count, - state->filename, state->nplurals); - switch (result) - { - case SAVE_ERR_CANT_ALLOC: - strcpy(state->error, errors[ERR_CANT_ALLOC]); - state->running = 0; - break; - case SAVE_ERR_CANT_OPEN_FILE: - strcpy(state->error, errors[ERR_CANT_SAVE]); - state->running = 0; - break; - case SAVE_ERR_CANT_MOVE_FILE: - strcpy(state->error, strerror(errno)); - state->running = 0; - break; - case SAVE_ERR_NONE: - default: - state->error[0] = 0; - } - */ - state->dirty = 0; + for (int key = 0; key < LEN(command_keys); key++) + if ((command_keys[key].ev.key + && (command_keys[key].ev.key == ev->key)) + || (command_keys[key].ev.ch + && (command_keys[key].ev.ch == ev->ch))) + if (command_keys[key].callback + && !command_keys[key].callback(state)) + return; } - else if (ev->key == TB_KEY_CTRL_Z || ev->ch == 'z') - ; // toggle_fuzzy(state); - else if (ev->ch == 'G') - move_bottom(state); - else if (ev->key == TB_KEY_END) - move_end(state); - else if (ev->key == TB_KEY_ENTER) + else { - /* - if (state->msgid_count == 0) - strcpy(state->error, errors[ERR_EMPTY_FILE]); - else + for (int key = 0; key < LEN(insert_keys); key++) + if ((insert_keys[key].ev.key + && (insert_keys[key].ev.key == ev->key)) + || (insert_keys[key].ev.ch + && (insert_keys[key].ev.ch == ev->ch))) + if (insert_keys[key].callback + && !insert_keys[key].callback(state)) + return; + if (ev->ch) { - state->msgstr_index = 0; - show_edit(state); + insert_char(state, ev->ch); + return; } - */ } - else if (ev->key == TB_KEY_F1 || ev->ch == 'H') + + strcpy(state->error, errors[ERR_UNKNOWN_KEY]); + /* +else if (ev->key == TB_KEY_CTRL_B || ev->key == TB_KEY_PGUP) + move_page_up(state); +else if (ev->key == TB_KEY_CTRL_F || ev->key == TB_KEY_PGDN) + move_page_down(state); +else if (ev->key == TB_KEY_CTRL_S || ev->ch == 'w') +{ + if (!state->dirty) + return; + int result = save_file(state->entries, state->real_msgid_count, + state->filename, state->nplurals); + switch (result) { - state->show_help = 1; - *state->error = 0; + case SAVE_ERR_CANT_ALLOC: + strcpy(state->error, errors[ERR_CANT_ALLOC]); + state->running = 0; + break; + case SAVE_ERR_CANT_OPEN_FILE: + strcpy(state->error, errors[ERR_CANT_SAVE]); + state->running = 0; + break; + case SAVE_ERR_CANT_MOVE_FILE: + strcpy(state->error, strerror(errno)); + state->running = 0; + break; + case SAVE_ERR_NONE: + default: + state->error[0] = 0; } - else if (ev->key == TB_KEY_F3 || ev->ch == 'n') - ; // next_match(state); - else if (ev->key == TB_KEY_F4 || ev->ch == 'N') - ; // prev_match(state); - else if (ev->key == TB_KEY_F7 || ev->ch == '/') - ; // show_search(state); - else if (ev->key == TB_KEY_F8 || ev->ch == 'u') - ; // next_untranslated(state); - else if (ev->key == TB_KEY_F9 || ev->ch == 'f') - ; // next_fuzzy(state); - else if (ev->ch == 'g') - move_top(state); - else if (ev->key == TB_KEY_HOME) - move_start(state); + state->dirty = 0; +} +else if (ev->key == TB_KEY_CTRL_Z || ev->ch == 'z') + ; // toggle_fuzzy(state); +else if (ev->ch == 'G') + move_bottom(state); +else if (ev->key == TB_KEY_END) + move_end(state); +else if (ev->key == TB_KEY_ENTER) +{ + if (state->msgid_count == 0) + strcpy(state->error, errors[ERR_EMPTY_FILE]); else - strcpy(state->error, errors[ERR_UNKNOWN_KEY]); + { + state->msgstr_index = 0; + show_edit(state); + } +} +else if (ev->key == TB_KEY_F1 || ev->ch == 'H') +{ + state->show_help = 1; + *state->error = 0; +} +else if (ev->key == TB_KEY_F3 || ev->ch == 'n') + ; // next_match(state); +else if (ev->key == TB_KEY_F4 || ev->ch == 'N') + ; // prev_match(state); +else if (ev->key == TB_KEY_F7 || ev->ch == '/') + ; // show_search(state); +else if (ev->key == TB_KEY_F8 || ev->ch == 'u') + ; // next_untranslated(state); +else if (ev->key == TB_KEY_F9 || ev->ch == 'f') + ; // next_fuzzy(state); +else if (ev->ch == 'g') + move_top(state); +else if (ev->key == TB_KEY_HOME) + move_start(state); +else + strcpy(state->error, errors[ERR_UNKNOWN_KEY]); + */ } void diff --git a/util.h b/util.h @@ -6,6 +6,14 @@ #define MIN(a, b) ((a < b) ? a : b) enum { + UNKNOWN_MODE, + COMMAND_MODE, + HELP_MODE, + INSERT_MODE, + PROMPT_MODE +}; + +enum { ERR_UNKNOWN_KEY, ERR_EXIT_KEY, ERR_DLG_OPEN,