sled

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

чување 56444dedb80e4697eb3c50dc4d3cc5f5522ca886
родитељ 15c5784059f1ad3a4798f0a7c92a59a078f5a619
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Sat, 19 Feb 2022 23:32:27 +0100

Add input dialog (and Go to line dialog)

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

Diffstat:
MTODO | 27++++++++++++++-------------
Mconfig.def.h | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mconfig.h | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mdraw.c | 124++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mdraw.h | 21++++++++++++++++++---
Msled.1.in | 2++
Msled.c | 463+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mutil.c | 6+++---
Mutil.h | 6+++---
измењених датотека: 9, додавања: 628(+), брисања: 191(-)

diff --git a/TODO b/TODO @@ -1,20 +1,9 @@ TODO ==== - [~] Create a working base by migration from poe - [x] Implement "inverse vi mode" - [~] Selections! - [x] Initial support for selections - (DrawState.in_selection) - [x] Selections modify behavior of eg. Delete key - and inserting characters - [ ] Copy/paste? - [ ] Add vi V - [~] Write operation; create backup files - [~] Convert to termbox2? - [ ] Test. + [ ] Copy/paste? [ ] Allow creating files (error out if directory not writeable, only create file when saving) @@ -23,10 +12,22 @@ [ ] Add vi H/L - [ ] Go to line # + [ ] Add vi V [ ] Test for binary when opening? + [x] Go to line # + + [x] Convert to termbox2? + + [x] Create a working base by migration from poe + [x] Implement "inverse vi mode" + [x] Selections! + [x] Initial support for selections + (DrawState.in_selection) + [x] Selections modify behavior of eg. Delete key + and inserting characters + < > Search? < > Replace? diff --git a/config.def.h b/config.def.h @@ -1,19 +1,23 @@ /* See the file LICENSE for copyright and license details. */ -#define DLG_FG TB_BLACK -#define DLG_BG TB_WHITE -#define ERROR_FG TB_WHITE | TB_BOLD -#define ERROR_BG TB_RED -#define PROMPT_FG TB_YELLOW | TB_BOLD -#define PROMPT_BG TB_MAGENTA -#define SEL_FG TB_BLACK -#define SEL_BG TB_WHITE -#define SHADOW_FG TB_BLACK -#define SHADOW_BG TB_BLACK | TB_BOLD -#define TEXT_FG TB_WHITE -#define TEXT_BG TB_DEFAULT -#define TITLE_FG TB_BLACK -#define TITLE_BG TB_WHITE +#define DLG_FG TB_BLACK +#define DLG_BG TB_WHITE +#define ERROR_FG TB_WHITE | TB_BOLD +#define ERROR_BG TB_RED +#define INPUT_FG TB_BLACK +#define INPUT_BG TB_WHITE +#define INPUT_EDIT_FG TB_WHITE +#define INPUT_EDIT_BG TB_BLACK +#define PROMPT_FG TB_YELLOW | TB_BOLD +#define PROMPT_BG TB_MAGENTA +#define SEL_FG TB_BLACK +#define SEL_BG TB_WHITE +#define SHADOW_FG TB_BLACK +#define SHADOW_BG TB_BLACK | TB_BOLD +#define TEXT_FG TB_WHITE +#define TEXT_BG TB_DEFAULT +#define TITLE_FG TB_BLACK +#define TITLE_BG TB_WHITE int cancel_close(struct DrawState* state); int enter_command_mode(struct DrawState* state); @@ -23,6 +27,20 @@ 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 input_cancel(struct DrawState* state); +int input_enter(struct DrawState* state); +int input_erase_backwards(struct DrawState* state); +int input_erase_forward(struct DrawState* state); +int input_erase_to_end(struct DrawState* state); +int input_erase_to_start(struct DrawState* state); +int input_insert_space(struct DrawState* state); +int input_insert_tab(struct DrawState* state); +int input_move_end(struct DrawState* state); +int input_move_left(struct DrawState* state); +int input_move_right(struct DrawState* state); +int input_move_start(struct DrawState* state); +int input_move_prev_word(struct DrawState* state); +int input_move_next_word(struct DrawState* state); int insert_line(struct DrawState* state); int insert_space(struct DrawState* state); int insert_tab(struct DrawState* state); @@ -39,11 +57,11 @@ int move_start(struct DrawState* state); int move_top(struct DrawState* state); int move_up(struct DrawState* state); int reread_or_callback(struct DrawState* state); +int show_go_to_line(struct DrawState* state); int show_help(struct DrawState* state); int toggle_selection(struct DrawState* state); int write_file(struct DrawState* state); - /* clang-format off */ /* Always processed */ @@ -77,6 +95,8 @@ static const struct Key global_keys[] = { .callback = move_page_down }, { .ev = { .key = TB_KEY_CTRL_K }, .callback = erase_to_end }, + { .ev = { .key = TB_KEY_CTRL_L }, + .callback = show_go_to_line }, { .ev = { .key = TB_KEY_CTRL_O }, .callback = reread_or_callback }, { .ev = { .key = TB_KEY_CTRL_R }, @@ -98,6 +118,40 @@ static const struct Key global_keys[] = { { .ev = { .key = TB_KEY_PGUP }, .callback = move_page_up } }; +/* Only when input dialog is shown */ +static const struct Key input_dialog_keys[] = { + { .ev = { .key = TB_KEY_ESC }, .callback = input_cancel }, + { .ev = { .key = TB_KEY_ARROW_LEFT }, + .callback = input_move_left }, + { .ev = { .key = TB_KEY_ARROW_RIGHT }, + .callback = input_move_right }, + { .ev = { .key = TB_KEY_BACKSPACE }, + .callback = input_erase_backwards }, + { .ev = { .key = TB_KEY_BACKSPACE2 }, + .callback = input_erase_backwards }, + { .ev = { .key = TB_KEY_CTRL_A }, + .callback = input_move_start }, + { .ev = { .key = TB_KEY_CTRL_E }, + .callback = input_move_end }, + { .ev = { .key = TB_KEY_CTRL_K }, + .callback = input_erase_to_end }, + { .ev = { .key = TB_KEY_CTRL_R }, + .callback = input_move_prev_word }, + { .ev = { .key = TB_KEY_CTRL_T }, + .callback = input_move_next_word }, + { .ev = { .key = TB_KEY_CTRL_U }, + .callback = input_erase_to_start }, + { .ev = { .key = TB_KEY_DELETE }, + .callback = input_erase_forward }, + { .ev = { .key = TB_KEY_END }, .callback = input_move_end }, + { .ev = { .key = TB_KEY_ENTER }, + .callback = input_enter }, + { .ev = { .key = TB_KEY_HOME }, .callback = input_move_start }, + { .ev = { .key = TB_KEY_SPACE }, + .callback = input_insert_space }, + { .ev = { .key = TB_KEY_TAB }, .callback = input_insert_tab } +}; + /* Only when in insert mode */ static const struct Key insert_keys[] = { { .ev = { .key = TB_KEY_CTRL_C }, @@ -113,6 +167,7 @@ static const struct Key insert_keys[] = { static const struct Key command_keys[] = { { .ev = { .key = TB_KEY_CTRL_C }, .callback = enter_insert_mode }, + { .ev = { .ch = ':' }, .callback = show_go_to_line }, { .ev = { .ch = '0' }, .callback = move_start }, { .ev = { .ch = '$' }, .callback = move_end }, { .ev = { .ch = 'g' }, .callback = move_top }, diff --git a/config.h b/config.h @@ -1,19 +1,23 @@ /* See the file LICENSE for copyright and license details. */ -#define DLG_FG TB_BLACK -#define DLG_BG TB_WHITE -#define ERROR_FG TB_WHITE | TB_BOLD -#define ERROR_BG TB_RED -#define PROMPT_FG TB_YELLOW | TB_BOLD -#define PROMPT_BG TB_MAGENTA -#define SEL_FG TB_BLACK -#define SEL_BG TB_WHITE -#define SHADOW_FG TB_BLACK -#define SHADOW_BG TB_BLACK | TB_BOLD -#define TEXT_FG TB_WHITE -#define TEXT_BG TB_DEFAULT -#define TITLE_FG TB_BLACK -#define TITLE_BG TB_WHITE +#define DLG_FG TB_BLACK +#define DLG_BG TB_WHITE +#define ERROR_FG TB_WHITE | TB_BOLD +#define ERROR_BG TB_RED +#define INPUT_FG TB_BLACK +#define INPUT_BG TB_WHITE +#define INPUT_EDIT_FG TB_WHITE +#define INPUT_EDIT_BG TB_BLACK +#define PROMPT_FG TB_YELLOW | TB_BOLD +#define PROMPT_BG TB_MAGENTA +#define SEL_FG TB_BLACK +#define SEL_BG TB_WHITE +#define SHADOW_FG TB_BLACK +#define SHADOW_BG TB_BLACK | TB_BOLD +#define TEXT_FG TB_WHITE +#define TEXT_BG TB_DEFAULT +#define TITLE_FG TB_BLACK +#define TITLE_BG TB_WHITE int cancel_close(struct DrawState* state); int enter_command_mode(struct DrawState* state); @@ -23,6 +27,20 @@ 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 input_cancel(struct DrawState* state); +int input_enter(struct DrawState* state); +int input_erase_backwards(struct DrawState* state); +int input_erase_forward(struct DrawState* state); +int input_erase_to_end(struct DrawState* state); +int input_erase_to_start(struct DrawState* state); +int input_insert_space(struct DrawState* state); +int input_insert_tab(struct DrawState* state); +int input_move_end(struct DrawState* state); +int input_move_left(struct DrawState* state); +int input_move_right(struct DrawState* state); +int input_move_start(struct DrawState* state); +int input_move_prev_word(struct DrawState* state); +int input_move_next_word(struct DrawState* state); int insert_line(struct DrawState* state); int insert_space(struct DrawState* state); int insert_tab(struct DrawState* state); @@ -39,11 +57,11 @@ int move_start(struct DrawState* state); int move_top(struct DrawState* state); int move_up(struct DrawState* state); int reread_or_callback(struct DrawState* state); +int show_go_to_line(struct DrawState* state); int show_help(struct DrawState* state); int toggle_selection(struct DrawState* state); int write_file(struct DrawState* state); - /* clang-format off */ /* Always processed */ @@ -77,6 +95,8 @@ static const struct Key global_keys[] = { .callback = move_page_down }, { .ev = { .key = TB_KEY_CTRL_K }, .callback = erase_to_end }, + { .ev = { .key = TB_KEY_CTRL_L }, + .callback = show_go_to_line }, { .ev = { .key = TB_KEY_CTRL_O }, .callback = reread_or_callback }, { .ev = { .key = TB_KEY_CTRL_R }, @@ -98,6 +118,40 @@ static const struct Key global_keys[] = { { .ev = { .key = TB_KEY_PGUP }, .callback = move_page_up } }; +/* Only when input dialog is shown */ +static const struct Key input_dialog_keys[] = { + { .ev = { .key = TB_KEY_ESC }, .callback = input_cancel }, + { .ev = { .key = TB_KEY_ARROW_LEFT }, + .callback = input_move_left }, + { .ev = { .key = TB_KEY_ARROW_RIGHT }, + .callback = input_move_right }, + { .ev = { .key = TB_KEY_BACKSPACE }, + .callback = input_erase_backwards }, + { .ev = { .key = TB_KEY_BACKSPACE2 }, + .callback = input_erase_backwards }, + { .ev = { .key = TB_KEY_CTRL_A }, + .callback = input_move_start }, + { .ev = { .key = TB_KEY_CTRL_E }, + .callback = input_move_end }, + { .ev = { .key = TB_KEY_CTRL_K }, + .callback = input_erase_to_end }, + { .ev = { .key = TB_KEY_CTRL_R }, + .callback = input_move_prev_word }, + { .ev = { .key = TB_KEY_CTRL_T }, + .callback = input_move_next_word }, + { .ev = { .key = TB_KEY_CTRL_U }, + .callback = input_erase_to_start }, + { .ev = { .key = TB_KEY_DELETE }, + .callback = input_erase_forward }, + { .ev = { .key = TB_KEY_END }, .callback = input_move_end }, + { .ev = { .key = TB_KEY_ENTER }, + .callback = input_enter }, + { .ev = { .key = TB_KEY_HOME }, .callback = input_move_start }, + { .ev = { .key = TB_KEY_SPACE }, + .callback = input_insert_space }, + { .ev = { .key = TB_KEY_TAB }, .callback = input_insert_tab } +}; + /* Only when in insert mode */ static const struct Key insert_keys[] = { { .ev = { .key = TB_KEY_CTRL_C }, @@ -113,6 +167,7 @@ static const struct Key insert_keys[] = { static const struct Key command_keys[] = { { .ev = { .key = TB_KEY_CTRL_C }, .callback = enter_insert_mode }, + { .ev = { .ch = ':' }, .callback = show_go_to_line }, { .ev = { .ch = '0' }, .callback = move_start }, { .ev = { .ch = '$' }, .callback = move_end }, { .ev = { .ch = 'g' }, .callback = move_top }, diff --git a/draw.c b/draw.c @@ -23,6 +23,7 @@ static const char* help[] = { "Home or C-A - Go to start of line", "C-C - Switch command <-> insert mode", "End or C-E - Go to end of line", + "C-L - Show Go to line dialog", "C-O - Reread (reopen) file", "C-Q - Quit", "C-R - Go to previous word", @@ -50,7 +51,11 @@ const char* errors[] = { [ERR_CANT_ALLOC] = "Memory allocation failed", [ERR_CANT_SAVE] = "File is not writeable", [ERR_CANT_OPEN] = "Cannot read file", - [ERR_CANT_MOVE] = "Cannot move temporary file"}; + [ERR_CANT_MOVE] = "Cannot move temporary file", + [ERR_INPUT_NONNUM] = "Input must be numerical", + [ERR_INVALID_NUM] = "Invalid input"}; +const char* go_to_dialog_title = "Go to line"; +const char* search_dialog_title = "Search text"; const char* program_name = "sled"; const char* prompt_dirty = "File changed, save [y/n/ESC]?"; const char* prompt_overwrite = "Not empty, overwrite? [y/n/ESC]?"; @@ -84,31 +89,40 @@ void init_drawstate(struct DrawState* state, char* error, char* filename, char* prompt) { - state->error = error; - state->filename = filename; - state->buffer = NULL; - state->column = 0; - state->display_column = 0; - state->first_shown_column = 0; - state->first_shown_row = 0; - state->row = 0; - state->rows_count = 0; - state->saved_column = -1; - state->help_width = calculate_help_width(state); - state->paste_buffer = NULL; - state->paste_rows_count = 0; - state->prompt = prompt; - state->prompt_callback = NULL; - state->running = 1; - state->dirty = 0; - state->in_prompt = 0; - state->in_command_mode = 0; - state->in_selection_mode = 0; - state->show_help = 0; - state->sel_start_row = 0; - state->sel_start_col = 0; - state->sel_end_row = 0; - state->sel_end_col = 0; + state->error = error; + state->filename = filename; + state->input = NULL; + state->buffer = NULL; + state->column = 0; + state->display_column = 0; + state->first_shown_column = 0; + state->first_shown_row = 0; + state->input_first_shown_column = 0; + state->input_display_column = 0; + state->input_column = 0; + state->input_title = NULL; + state->row = 0; + state->rows_count = 0; + state->saved_column = -1; + state->help_width = calculate_help_width(state); + state->input_dialog_width = calculate_input_dialog_width(state); + state->paste_buffer = NULL; + state->paste_rows_count = 0; + state->prompt = prompt; + state->prompt_callback = NULL; + state->input_callback = NULL; + state->running = 1; + state->dirty = 0; + state->in_prompt = 0; + state->in_command_mode = 0; + state->in_selection_mode = 0; + state->show_help = 0; + state->show_input_dialog = 0; + state->input_numerical = 0; + state->sel_start_row = 0; + state->sel_start_col = 0; + state->sel_end_row = 0; + state->sel_end_col = 0; } void @@ -121,6 +135,7 @@ free_drawstate(struct DrawState* state) for (row = 0; row < state->paste_rows_count; row++) free_bufferline(state->paste_buffer + row); free(state->paste_buffer); + free(state->input); } /* Calculates display length of a buffer line. @@ -174,6 +189,22 @@ calculate_help_width(const struct DrawState* state) return state->maxx > HELP_WIDTH + 2 ? HELP_WIDTH : state->maxx - 2; } +int +calculate_input_dialog_width(const struct DrawState* state) +{ + return state->maxx > INPUT_DLG_WIDTH + 2 ? INPUT_DLG_WIDTH + : state->maxx - 2; +} + +void +draw_rect(const int startx, const int endx, const int starty, const int endy, + const uint16_t fg, const uint16_t bg) +{ + for (int y = starty; y < endy; y++) + for (int x = startx; x < endx; x++) + tb_change_cell(x, y, ' ', fg, bg); +} + void draw_box(const int startx, const int endx, const int starty, const int endy, const uint16_t fg, const uint16_t bg) @@ -496,6 +527,43 @@ draw_help(const struct DrawState* state) } const struct DrawState* +draw_input_dialog(const struct DrawState* state) +{ + int startx, endx, starty, endy; + int w = state->input_dialog_width; + int h = INPUT_DLG_HEIGHT; + int maxx = state->maxx; + int maxy = state->maxy; + size_t len = u32_strlen(state->input); + size_t dlen = 0; + + startx = w > maxx ? 0 : (maxx - w) / 2; + endx = w > maxx ? maxx - 1 : maxx - startx; + + starty = maxy - 1 - h; + endy = maxy - 1; + + draw_box(startx, endx, starty, endy, INPUT_FG, INPUT_BG); + draw_rect(startx + 1, endx - 1, starty + 1, endy - 2, INPUT_EDIT_FG, + INPUT_EDIT_BG); + if (state->input_title) + if (!draw_string(startx, starty, TITLE_FG, TITLE_BG, + state->input_title, strlen(state->input_title) + 2, + w, 1, 1, CENTER, 0, 0, NULL, 0, 0, 0, 0, 0, 0)) + return NULL; + dlen = display_length(state, state->input, len); + if (dlen > state->input_first_shown_column) + u32_draw_string(startx + 1, starty + 1, INPUT_EDIT_FG, + INPUT_EDIT_BG, state->input, w - 2, w - 2, 1, 1, LEFT, + state->input_first_shown_column, 1, NULL, 0, 0, 0, 0, 0, + 0); + tb_set_cursor(startx + 2 + state->input_display_column + - state->input_first_shown_column, + starty + 1); + return state; +} + +const struct DrawState* draw_screen(const struct DrawState* state) { const struct BufferLine* current = NULL; @@ -640,6 +708,10 @@ draw(const struct DrawState* state) if (!draw_help(state)) return NULL; + if (state->show_input_dialog) + if (!draw_input_dialog(state)) + return NULL; + if (!draw_status(state)) return NULL; diff --git a/draw.h b/draw.h @@ -14,9 +14,12 @@ #define BORDER_VER ((uint32_t)L'\u2502') #define BORDER_HOR ((uint32_t)L'\u2500') -#define HELP_WIDTH 60 -#define HELP_HEIGHT 21 -#define TAB_SIZE 8 +#define HELP_WIDTH 60 +#define HELP_HEIGHT 21 +#define INPUT_DLG_WIDTH 20 +#define INPUT_DLG_HEIGHT 4 +#define TAB_SIZE 8 +#define MAXINPUT (512 + 1) typedef enum { LEFT, CENTER, RIGHT } Alignment; @@ -31,25 +34,34 @@ struct DrawState { char* error; char* prompt; char* filename; + uint32_t* input; struct BufferLine* buffer; size_t buffer_size; int column; int row; int first_shown_column; int first_shown_row; + int input_first_shown_column; + int input_display_column; + int input_column; + char* input_title; int rows_count; int display_column; int saved_column; int help_width; + int input_dialog_width; struct BufferLine* paste_buffer; int paste_rows_count; void (*prompt_callback)(struct DrawState*, struct tb_event*); + void (*input_callback)(struct DrawState*); int running; int dirty; int in_prompt; int in_command_mode; int in_selection_mode; int show_help; + int show_input_dialog; + int input_numerical; int maxx; int maxy; size_t sel_start_row; @@ -81,6 +93,9 @@ size_t display_length(const struct DrawState* state, uint32_t* buffer, size_t buffer_length(const struct DrawState* state, uint32_t* buffer, const size_t max_index); int calculate_help_width(const struct DrawState* state); +int calculate_input_dialog_width(const struct DrawState* state); +void draw_rect(const int startx, const int endx, const int starty, + const int endy, const uint16_t fg, const uint16_t bg); void draw_box(const int startx, const int endx, const int starty, const int endy, const uint16_t fg, const uint16_t bg); const char* draw_string(const int x, const int y, const uint16_t fg, diff --git a/sled.1.in b/sled.1.in @@ -95,6 +95,7 @@ _ \fCEnd\fP or \fCC-E\fP@Go to end of line \fCPgDown\fP or \fCC-F\fP@Go to next page \fCC-K\fP@Erase to end of line +\fCC-L\fP@Show Go to line dialog \fCC-O\fP@Reread (reopen) file \fCC-Q\fP@Quit \fCC-R\fP@Go to previous word @@ -124,6 +125,7 @@ box tab(@); l l. \fBKey\fP@\fBAction\fP _ +\fC:\fP@Show Go to line dialog \fC0\fP@Go to start of line \fC$\fP@Go to end of line \fCG\fP@Go to last line in the file diff --git a/sled.c b/sled.c @@ -39,6 +39,8 @@ enum { extern const char* errors[]; extern const char* mode_labels[]; +extern const char* go_to_dialog_title; +extern const char* search_dialog_title; extern const char* prompt_dirty; extern const char* prompt_overwrite; extern const char* prompt_reread_dirty; @@ -54,9 +56,6 @@ void format_position(char* result, size_t result_size, const char* format, void format_msgs(char* result, size_t result_size, const char* format, const struct DrawState* state); void reread_file(struct DrawState* state); -void overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev); -void overwrite_paste_callback(struct DrawState* state, struct tb_event* ev); -void cancel_callback(struct DrawState* state, struct tb_event* ev); void reread_callback(struct DrawState* state, struct tb_event* ev); void quit_callback(struct DrawState* state, struct tb_event* ev); struct BufferLine* load_info(struct DrawState* state); @@ -69,9 +68,7 @@ int toggle_selection(struct DrawState* state); int clear_selection(struct DrawState* state); int position_to_selection_start(struct DrawState* state); int erase_selection(struct DrawState* state); -void handle_editbox_key_event(struct tb_event* ev, struct DrawState* state); void handle_key_event(struct tb_event* ev, struct DrawState* state); -void handle_search_key_event(struct tb_event* ev, struct DrawState* state); int print_error(const int code, const char* msg, ...) @@ -173,6 +170,48 @@ reread_file(struct DrawState* state) } void +go_to_line_callback(struct DrawState* state) +{ + size_t lineno = 0; + size_t len = 0; + char* u8_input = NULL; + + if (!state) + return; + + if (*state->input) + { + len = u32_strlen(state->input); + u8_input = calloc(len + 1, UTF8REPMAX); + if (!u8_input) + { + strcpy(state->error, errors[ERR_CANT_ALLOC]); + return; + } + unicode_string_to_u8(u8_input, state->input, len + 1); + errno = 0; + lineno = strtoul(u8_input, NULL, 10); + if (errno) + strcpy(state->error, errors[ERR_INVALID_NUM]); + else if (lineno + 1 > state->rows_count) + move_bottom(state); + else + { + state->row = lineno - 1; + if (state->first_shown_row > state->row) + state->first_shown_row = state->row; + else if (state->first_shown_row + (state->maxy - 2) + < state->row) + state->first_shown_row + = state->row - (state->maxy - 2); + } + free(u8_input); + *state->input = 0; + } + state->input_callback = NULL; +} + +void reread_callback(struct DrawState* state, struct tb_event* ev) { if (!state) @@ -666,6 +705,51 @@ move_page_down(struct DrawState* state) } int +input_insert_char(struct DrawState* state, uint32_t ch) +{ + size_t len = u32_strlen(state->input); + if (state->input_column < MAXINPUT - 1) + { + size_t i = len + 1; + int add_null = state->input[state->input_column] == 0; + while (i < MAXINPUT - 2 && i > state->input_column) + { + state->input[i] = state->input[i - 1]; + i--; + } + state->input[state->input_column++] = ch; + if (state->input_column == MAXINPUT) + state->input_column = MAXINPUT - 1; + state->input_display_column = display_length(state, + state->input, state->input_column); + if (state->input_display_column - state->input_first_shown_column + > state->input_dialog_width - 4) + { + state->input_first_shown_column + = state->input_display_column + - state->input_dialog_width + 4; + state->input_display_column = display_length(state, + state->input, state->input_column); + } + if (add_null) + state->input[state->input_column] = 0; + } + return 0; +} + +int +input_insert_space(struct DrawState* state) +{ + return input_insert_char(state, ' '); +} + +int +input_insert_tab(struct DrawState* state) +{ + return input_insert_char(state, '\t'); +} + +int insert_char(struct DrawState* state, uint32_t ch) { struct BufferLine* buffer_line = NULL; @@ -1024,6 +1108,208 @@ enter_insert_mode(struct DrawState* state) } int +input_cancel(struct DrawState* state) +{ + state->show_input_dialog = 0; + *state->input = 0; + return 0; +} + +int +input_enter(struct DrawState* state) +{ + if (state->input_callback) + state->input_callback(state); + state->show_input_dialog = 0; + return 0; +} + +int +input_erase_backwards(struct DrawState* state) +{ + if (state->input_column > 0) + { + size_t i = state->input_column; + do + { + state->input[i - 1] = state->input[i]; + i++; + } while (state->input[i]); + state->input[i - 1] = 0; + state->input_column--; + if (state->input_first_shown_column > 0) + { + if (display_length(state, state->input, + u32_strlen(state->input)) + >= state->maxx - 1) + state->input_first_shown_column--; + } + state->input_display_column = display_length(state, + state->input, state->input_column); + if (state->input_display_column + < state->input_first_shown_column) + state->input_first_shown_column + = state->input_display_column; + state->dirty = 1; + } + return 0; +} + +int +input_erase_forward(struct DrawState* state) +{ + if (state->input) + { + size_t i = state->input_column; + while (i < MAXBUFLINE - 2 && state->input[i + 1]) + { + state->input[i] = state->input[i + 1]; + i++; + } + state->input[i] = 0; + state->dirty = 1; + } + return 0; +} + +int input_move_end(struct DrawState* state); + +int +input_erase_to_end(struct DrawState* state) +{ + state->input[state->input_column] = 0; + input_move_end(state); + state->dirty = 1; + return 0; +} + +int input_move_start(struct DrawState* state); + +int +input_erase_to_start(struct DrawState* state) +{ + size_t i = state->input_column; + size_t len = u32_strlen(state->input); + + do + { + state->input[i - state->input_column] = state->input[i]; + i++; + } while (state->input[i]); + state->input[len - state->input_column] = 0; + input_move_start(state); + state->dirty = 1; + return 0; +} + +int +input_move_end(struct DrawState* state) +{ + size_t len = u32_strlen(state->input); + size_t dlen; + + state->input_column = len; + dlen = display_length(state, state->input, state->input_column); + state->input_display_column = dlen; + if (dlen > state->maxx - 4) + state->input_first_shown_column = dlen - state->maxx + 4; + else + state->input_first_shown_column = 0; + return 0; +} + +int +input_move_left(struct DrawState* state) +{ + if (state->input_column == 0) + return 0; + + if (state->input[state->input_column - 1] == '\t') + state->input_display_column = display_length(state, + state->input, state->input_column - 1); + else + state->input_display_column--; + state->input_column--; + + if (state->input_display_column < state->input_first_shown_column) + state->input_first_shown_column = state->input_display_column; + return 0; +} + +int +input_move_right(struct DrawState* state) +{ + size_t len = u32_strlen(state->input); + if (state->input_column == len) + return 0; + + if (state->input[state->input_column] == '\t') + state->input_display_column = display_length(state, + state->input, state->input_column + 1); + else + state->input_display_column++; + state->input_column++; + + if (state->input_display_column - state->input_first_shown_column + > state->maxx - 1) + state->input_first_shown_column + = state->input_display_column - state->maxx + 1; + return 0; +} + +int +input_move_start(struct DrawState* state) +{ + state->input_column = 0; + state->input_display_column = 0; + state->input_first_shown_column = 0; + return 0; +} + +int +input_move_prev_word(struct DrawState* state) +{ + int i = state->input_column; + uint32_t* pline = state->input; + + if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) + i--; + while (i > 0 && u32_is_word_boundary(pline[i], 0)) + i--; + while (i > 0 && !u32_is_word_boundary(pline[i], 0) + && !u32_is_word_boundary(pline[i - 1], 0)) + i--; + + state->input_column = i; + state->input_display_column + = display_length(state, state->input, state->input_column); + if (state->input_display_column < state->input_first_shown_column) + state->input_first_shown_column = state->input_display_column; + return 0; +} + +int +input_move_next_word(struct DrawState* state) +{ + int i = state->input_column; + uint32_t* pline = state->input; + size_t len = u32_strlen(state->input); + + while (i < len && !u32_is_word_boundary(pline[i], 0)) + i++; + while (i < len && u32_is_word_boundary(pline[i], 0)) + i++; + state->input_column = i; + state->input_display_column + = display_length(state, state->input, state->input_column); + if (state->input_display_column - state->input_first_shown_column + > state->maxx - 1) + state->input_first_shown_column + = state->input_display_column - state->maxx + 1; + return 0; +} + +int exit_program(struct DrawState* state) { if (state->dirty) @@ -1039,6 +1325,29 @@ exit_program(struct DrawState* state) } int +show_go_to_line(struct DrawState* state) +{ + state->input_title = (char*)go_to_dialog_title; + state->input_callback = go_to_line_callback; + if (!state->input) + { + if (!(state->input = calloc(MAXINPUT, sizeof(uint32_t)))) + { + strcpy(state->error, errors[ERR_CANT_ALLOC]); + return 0; + } + } + state->input_numerical = 1; + state->show_input_dialog = 1; + state->input_column = 0; + state->input_display_column = 0; + state->input_first_shown_column = 0; + *state->input = 0; + *state->error = 0; + return 0; +} + +int show_help(struct DrawState* state) { state->show_help = 1; @@ -1167,8 +1476,12 @@ erase_selection(struct DrawState* state) } void -handle_editbox_key_event(struct tb_event* ev, struct DrawState* state) +handle_key_event(struct tb_event* ev, struct DrawState* state) { + const struct Key* current = NULL; + if (!state) + return; + if (state->in_prompt) { if (ev->ch == 'y' || ev->ch == 'n') @@ -1179,76 +1492,15 @@ handle_editbox_key_event(struct tb_event* ev, struct DrawState* state) *state->prompt = 0; } else if (ev->key == TB_KEY_ESC) - { - state->in_prompt = 0; - *state->prompt = 0; - } - return; - } - - if (*state->error) - *state->error = 0; - - if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP) - state->saved_column = -1; - - if (ev->key == TB_KEY_ARROW_DOWN) - move_down(state); - else if (ev->key == TB_KEY_ARROW_LEFT) - move_left(state); - else if (ev->key == TB_KEY_ARROW_RIGHT) - move_right(state); - else if (ev->key == TB_KEY_ARROW_UP) - move_up(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_Q) - strcpy(state->error, errors[ERR_DLG_OPEN_QUIT]); - else if (ev->key == TB_KEY_CTRL_R) - move_prev_word(state); - else if (ev->key == TB_KEY_CTRL_T) - move_next_word(state); - else if (ev->key == TB_KEY_CTRL_U) - erase_to_start(state); - else if (ev->key == TB_KEY_CTRL_V) - paste_paste_buffer_to_input(state); - else if (ev->key == TB_KEY_CTRL_W) - erase_prev_word(state); - else if (ev->key == TB_KEY_CTRL_Z) - ; // toggle_fuzzy(state); - else if (ev->key == TB_KEY_END || ev->key == TB_KEY_CTRL_E) - move_end(state); - else if (ev->key == TB_KEY_ENTER) - { - if (insert_line(state)) - { - strcpy(state->error, errors[ERR_CANT_ALLOC]); - state->running = 0; - } - } -} - -void -handle_key_event(struct tb_event* ev, struct DrawState* state) -{ - const struct Key* current = NULL; - if (!state) - return; - - 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; + cancel_close(state); return; } /* 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 + && ev->key != TB_KEY_PGDN && ev->key != TB_KEY_PGUP + && ev->key != TB_KEY_CTRL_B && ev->key != TB_KEY_CTRL_F && !(state->in_command_mode && strchr("jkgG", ev->ch))) state->saved_column = -1; @@ -1261,9 +1513,6 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) return; } - if (state->in_prompt) - return; - if (state->show_help) { strcpy(state->error, errors[ERR_DLG_OPEN]); @@ -1279,7 +1528,32 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) return; } - if (state->in_command_mode) + if (state->show_input_dialog) + { + for (int key = 0; key < LEN(input_dialog_keys); key++) + { + current = &input_dialog_keys[key]; + if (((current->ev.key == ev->key) && !ev->ch) + || (current->ev.ch + && (current->ev.ch == ev->ch))) + if (current->callback + && !current->callback(state)) + return; + } + if (ev->ch) + { + if ((state->input_numerical + && u32_strchr( + (const uint32_t*)L"0123456789", + ev->ch)) + || !state->input_numerical) + input_insert_char(state, ev->ch); + else + strcpy(state->error, errors[ERR_INPUT_NONNUM]); + return; + } + } + else if (state->in_command_mode) { for (int key = 0; key < LEN(command_keys); key++) { @@ -1314,45 +1588,6 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) strcpy(state->error, errors[ERR_UNKNOWN_KEY]); } -void -handle_search_key_event(struct tb_event* ev, struct DrawState* state) -{ - if (!state) - return; - /* - if (ev->key == TB_KEY_ARROW_LEFT) - ;//move_search_left(state); - else if (ev->key == TB_KEY_ARROW_RIGHT) - ;//move_search_right(state); - else if ((ev->key == TB_KEY_BACKSPACE || ev->key == - TB_KEY_BACKSPACE2)) - ;//erase_search_backwards(state); - else if (ev->key == TB_KEY_CTRL_K) - erase_search_to_end(state); - else if (ev->key == TB_KEY_CTRL_R) - move_search_prev_word(state); - else if (ev->key == TB_KEY_CTRL_T) - move_search_next_word(state); - else if (ev->key == TB_KEY_CTRL_U) - erase_search_to_start(state); - else if (ev->key == TB_KEY_CTRL_W) - erase_search_prev_word(state); - else if (ev->key == TB_KEY_DELETE) - erase_search_forward(state); - else if (ev->key == TB_KEY_END || ev->key == - TB_KEY_CTRL_E) move_search_end(state); else if (ev->key == - TB_KEY_ENTER) hide_search(state); else if (ev->key == - TB_KEY_ESC) cancel_search(state); else if (ev->key == - TB_KEY_F3) next_match(state); else if (ev->key == TB_KEY_F4) - prev_match(state); - else if (ev->key == TB_KEY_HOME || ev->key == - TB_KEY_CTRL_A) move_search_start(state); else if (ev->key == - TB_KEY_SPACE) insert_search_char(state, ' '); else if - (ev->key == TB_KEY_TAB) insert_search_char(state, '\t'); else - if (ev->ch != 0) insert_search_char(state, ev->ch); - */ -} - int main(int argc, char** argv) { @@ -1424,6 +1659,8 @@ main(int argc, char** argv) { state.maxx = maxx; state.help_width = calculate_help_width(&state); + state.input_dialog_width + = calculate_input_dialog_width(&state); } state.maxy = tb_height(); diff --git a/util.c b/util.c @@ -68,10 +68,10 @@ u32_strlen(const uint32_t* s) return len; } -uint32_t* -u32_strchr(uint32_t* haystack, const uint32_t needle) +const uint32_t* +u32_strchr(const uint32_t* haystack, const uint32_t needle) { - uint32_t* ph = haystack; + const uint32_t* ph = haystack; while (*ph) { if (*ph == needle) diff --git a/util.h b/util.h @@ -24,8 +24,8 @@ enum { ERR_CANT_SAVE, ERR_CANT_OPEN, ERR_CANT_MOVE, - ERR_ILLEGAL_ON_FIRST, - ERR_NO_PLURAL_FORMS + ERR_INPUT_NONNUM, + ERR_INVALID_NUM }; #define ALLOC_DELTA 50 @@ -42,7 +42,7 @@ enum { size_t u8_string_to_unicode(uint32_t* us, const char* s, const size_t max); size_t unicode_string_to_u8(char* s, const uint32_t* us, const size_t max); size_t u32_strlen(const uint32_t* s); -uint32_t* u32_strchr(uint32_t* haystack, const uint32_t needle); +const uint32_t* u32_strchr(const uint32_t* haystack, const uint32_t needle); size_t u32_strcpy(uint32_t* to, const uint32_t* from); size_t u32_strncpy(uint32_t* to, const uint32_t* from, size_t max); size_t u32_strcat(uint32_t* to, const uint32_t* from);