sled

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

чување 7e7c9fd4c07e715b2b78b13a9243c73f3331744a
родитељ b1182c444d3807b27a56b00be640754bda63a856
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Tue, 15 Feb 2022 20:31:14 +0100

Initial selection support

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

Diffstat:
MTODO | 8+++++---
Mconfig.def.h | 11+++++++++++
Mconfig.h | 11+++++++++++
Mdraw.c | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mdraw.h | 19++++++++++++++-----
Msled.c | 270+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mutil.h | 6++++--
измењених датотека: 7, додавања: 324(+), брисања: 122(-)

diff --git a/TODO b/TODO @@ -3,9 +3,11 @@ [~] Create a working base by migration from poe [x] Implement "inverse vi mode" - [ ] Selections! - [ ] Selections modify behavior of eg. Delete key -> - DrawState.in_selection is needed + [~] Selections! + [x] Initial support for selections + (DrawState.in_selection) + [ ] Selections modify behavior of eg. Delete key + and inserting characters [ ] Copy/paste? diff --git a/config.def.h b/config.def.h @@ -6,6 +6,8 @@ #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 @@ -36,6 +38,9 @@ int move_start(struct DrawState* state); int move_top(struct DrawState* state); int move_up(struct DrawState* state); int show_help(struct DrawState* state); +int toggle_selection(struct DrawState* state); + +/* clang-format off */ /* Always processed */ static const struct Key top_level_keys[] = { @@ -70,6 +75,8 @@ static const struct Key global_keys[] = { .callback = erase_to_end }, { .ev = { .key = TB_KEY_CTRL_R }, .callback = move_prev_word }, + { .ev = { .key = TB_KEY_CTRL_SPACE }, + .callback = toggle_selection }, { .ev = { .key = TB_KEY_CTRL_T }, .callback = move_next_word }, { .ev = { .key = TB_KEY_CTRL_U }, @@ -105,11 +112,13 @@ static const struct Key command_keys[] = { { .ev = { .ch = 'k' }, .callback = move_up }, { .ev = { .ch = 'l' }, .callback = move_right }, { .ev = { .ch = 'q' }, .callback = exit_program }, + { .ev = { .ch = 'v' }, .callback = toggle_selection }, { .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 } }; +/* clang-format on */ void format_filename(char* result, size_t result_size, const char* format, const struct DrawState* state); @@ -118,6 +127,7 @@ void format_mode(char* result, size_t result_size, const char* format, void format_position(char* result, size_t result_size, const char* format, const struct DrawState* state); +/* clang-format off */ static const struct StatusSegment status_segments[] = { {.format = "%s%s", .callback = format_filename, @@ -140,3 +150,4 @@ static const struct StatusSegment status_segments[] = { .bg = TB_DEFAULT, .alignment = RIGHT}, }; +/* clang-format on */ diff --git a/config.h b/config.h @@ -6,6 +6,8 @@ #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 @@ -36,6 +38,9 @@ int move_start(struct DrawState* state); int move_top(struct DrawState* state); int move_up(struct DrawState* state); int show_help(struct DrawState* state); +int toggle_selection(struct DrawState* state); + +/* clang-format off */ /* Always processed */ static const struct Key top_level_keys[] = { @@ -70,6 +75,8 @@ static const struct Key global_keys[] = { .callback = erase_to_end }, { .ev = { .key = TB_KEY_CTRL_R }, .callback = move_prev_word }, + { .ev = { .key = TB_KEY_CTRL_SPACE }, + .callback = toggle_selection }, { .ev = { .key = TB_KEY_CTRL_T }, .callback = move_next_word }, { .ev = { .key = TB_KEY_CTRL_U }, @@ -105,11 +112,13 @@ static const struct Key command_keys[] = { { .ev = { .ch = 'k' }, .callback = move_up }, { .ev = { .ch = 'l' }, .callback = move_right }, { .ev = { .ch = 'q' }, .callback = exit_program }, + { .ev = { .ch = 'v' }, .callback = toggle_selection }, { .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 } }; +/* clang-format on */ void format_filename(char* result, size_t result_size, const char* format, const struct DrawState* state); @@ -118,6 +127,7 @@ void format_mode(char* result, size_t result_size, const char* format, void format_position(char* result, size_t result_size, const char* format, const struct DrawState* state); +/* clang-format off */ static const struct StatusSegment status_segments[] = { {.format = "%s%s", .callback = format_filename, @@ -140,3 +150,4 @@ static const struct StatusSegment status_segments[] = { .bg = TB_DEFAULT, .alignment = RIGHT}, }; +/* clang-format on */ diff --git a/draw.c b/draw.c @@ -14,25 +14,28 @@ /* clang-format off */ static const char* help_title = "sled help"; static const char* help[] = { - "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", + "Esc - Close dialog, clear error, cancel", + " selection", + "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-Space - Toggle selection mode", + "C-Q - Quit", "", - "Arrow keys work both in insert and command mode.", - "Some vi-like keys work in command mode.", + "Arrow keys work in insert, selection and command modes.", + "Some vi-like keys work in command and selection modes.", "", "See the manual (man sled) for full help."}; const char* mode_labels[] = { - [UNKNOWN_MODE] = "UNKNOWN", - [COMMAND_MODE] = "COMMAND", - [HELP_MODE] = "HELP", - [INSERT_MODE] = "INSERT", - [PROMPT_MODE] = "PROMPT" + [UNKNOWN_MODE] = "UNKNOWN", + [COMMAND_MODE] = "COMMAND", + [HELP_MODE] = "HELP", + [INSERT_MODE] = "INSERT", + [PROMPT_MODE] = "PROMPT", + [SELECTION_MODE] = "SELECTION" }; const char* errors[] = { [ERR_UNKNOWN_KEY] = "Unknown key (press F1 for help)", @@ -94,7 +97,12 @@ init_drawstate(struct DrawState* state, char* error, char* filename, 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; } void @@ -209,19 +217,25 @@ draw_box(const int startx, const int endx, const int starty, const int endy, * srch - search string; if empty or NULL no highlighting happens * sfg - fg color of search highlght * sbg - bg color of search highlight + * ssi - selection start index (>=) + * sei - selection end index (<) + * slfg - selection fg color + * slbg - selection bg color * */ const char* draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, const char* s, const int mc, const int fc, const int p_l, const int p_r, const Alignment align, const int ds, const int fill, - const uint32_t* srch, const uint16_t sfg, const uint16_t sbg) + const uint32_t* srch, const uint16_t sfg, const uint16_t sbg, + const size_t ssi, const size_t sei, const uint16_t slfg, + const uint16_t slbg) { uint32_t* us = calloc(strlen(s) + 1, sizeof(uint32_t)); if (!us) return NULL; u8_string_to_unicode(us, s, mc); u32_draw_string(x, y, fg, bg, us, mc, fc, p_l, p_r, align, ds, fill, - srch, sfg, sbg); + srch, sfg, sbg, ssi, sei, slfg, slbg); free(us); return s; } @@ -237,12 +251,18 @@ draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, * srch - search string; if empty or NULL no highlighting happens * sfg - fg color of search highlght * sbg - bg color of search highlight + * ssi - selection start index (>=) + * sei - selection end index (<) + * slfg - selection fg color + * slbg - selection bg color * */ void u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, const uint32_t* s, const int mc, const int fc, const int p_l, const int p_r, const Alignment align, const int ds, const int fill, - const uint32_t* srch, const uint16_t sfg, const uint16_t sbg) + const uint32_t* srch, const uint16_t sfg, const uint16_t sbg, + const size_t ssi, const size_t sei, const uint16_t slfg, + const uint16_t slbg) { /* * <-------- visible part ----------> @@ -318,6 +338,12 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, * */ while (ps && *ps && sc < c) { + if ((ps - s >= ssi) && (ps - s < sei)) + { + cfg = slfg; + cbg = slbg; + } + if (srch && *srch && hctl == 0) { if (u32_starts_with(ps, srch)) @@ -458,13 +484,13 @@ draw_help(const struct DrawState* state) draw_box(startx, endx, starty, endy, DLG_FG, DLG_BG); if (!draw_string(startx, starty, TITLE_FG, TITLE_BG, help_title, strlen(help_title) + 2, w - 2, 1, 1, CENTER, 0, 0, NULL, 0, - 0)) + 0, 0, 0, 0, 0)) return NULL; while (row < LEN(help)) { if (!draw_string(startx + 1, starty + 1 + row, DLG_FG, DLG_BG, help[row], w - 2, w - 2, 1, 1, LEFT, 0, 1, NULL, 0, - 0)) + 0, 0, 0, 0, 0)) return NULL; row++; } @@ -478,6 +504,15 @@ draw_screen(const struct DrawState* state) int max_screen_lines = state->maxy - 1; int cy = 0; size_t i = state->first_shown_row; + /* Selection start/end index - passed to u32_draw_string */ + size_t ssi = 0; + size_t sei = 0; + /* Selection start row/col */ + size_t ssr = 0; + size_t ssc = 0; + /* Selection end row/col */ + size_t ser = 0; + size_t sec = 0; if (!state) return NULL; @@ -490,10 +525,46 @@ draw_screen(const struct DrawState* state) { current = state->buffer + i; + ssi = 0; + sei = 0; + + if (state->sel_start_row < state->sel_end_row) + { + ssr = state->sel_start_row; + ssc = state->sel_start_col; + ser = state->sel_end_row; + sec = state->sel_end_col; + } + else /* Swap */ + { + ssr = state->sel_end_row; + ssc = state->sel_end_col; + ser = state->sel_start_row; + sec = state->sel_start_col; + } + if ((i > ssr) && (i < ser)) + { + ssi = 0; + sei = current->length; + } + else + { + if (i == ssr) + { + ssi = ssc; + if (i != ser) + sei = current->length; + } + if (i == ser) + { + sei = sec; + } + } + if (current->text) u32_draw_string(0, cy, TEXT_FG, TEXT_BG, current->text, state->maxx, state->maxx, 0, 0, CENTER, 0, 0, - NULL, 0, 0); + NULL, 0, 0, ssi, sei, SEL_FG, SEL_BG); i++; cy++; } @@ -519,14 +590,15 @@ draw_status(const struct DrawState* state) { if (!draw_string(0, state->maxy - 1, PROMPT_FG, PROMPT_BG, state->prompt, strlen(state->prompt) + 2, - state->maxx, 1, 1, CENTER, 0, 1, NULL, 0, 0)) + state->maxx, 1, 1, CENTER, 0, 1, NULL, 0, 0, 0, 0, + 0, 0)) return NULL; } else if (state->error && state->error[0]) { if (!draw_string(0, state->maxy - 1, ERROR_FG, ERROR_BG, state->error, strlen(state->error) + 2, state->maxx, - 1, 1, CENTER, 0, 1, NULL, 0, 0)) + 1, 1, CENTER, 0, 1, NULL, 0, 0, 0, 0, 0, 0)) return NULL; } else @@ -547,7 +619,8 @@ draw_status(const struct DrawState* state) pseg->fg, pseg->bg, buf, MIN(seg_size, strlen(buf) + 2), last ? seg_size + 1 : seg_size, 1, 1, - pseg->alignment, 0, 1, NULL, 0, 0)) + pseg->alignment, 0, 1, NULL, 0, 0, 0, 0, 0, + 0)) return NULL; pseg++; current_start += seg_size; diff --git a/draw.h b/draw.h @@ -10,9 +10,9 @@ #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 TAB_SIZE 8 typedef enum { LEFT, CENTER, RIGHT } Alignment; @@ -44,9 +44,14 @@ struct DrawState { int dirty; int in_prompt; int in_command_mode; + int in_selection_mode; int show_help; int maxx; int maxy; + size_t sel_start_row; + size_t sel_start_col; + size_t sel_end_row; + size_t sel_end_col; }; struct Key { @@ -78,12 +83,16 @@ const char* draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, const char* s, const int max_cols, const int fill_cols, const int padding_left, const int padding_right, const Alignment align, const int ds, const int fill, - const uint32_t* srch, const uint16_t sfg, const uint16_t sbg); + const uint32_t* srch, const uint16_t sfg, const uint16_t sbg, + const size_t ssi, const size_t sei, const uint16_t slfg, + const uint16_t slbg); void u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, const uint32_t* s, const int max_cols, const int fill_cols, const int padding_left, const int padding_right, const Alignment align, const int ds, const int fill, - const uint32_t* srch, const uint16_t sfg, const uint16_t sbg); + const uint32_t* srch, const uint16_t sfg, const uint16_t sbg, + const size_t ssi, const size_t sei, const uint16_t slfg, + const uint16_t slbg); const struct DrawState* draw_help(const struct DrawState* state); const struct DrawState* draw_screen(const struct DrawState* state); const struct DrawState* draw_status(const struct DrawState* state); diff --git a/sled.c b/sled.c @@ -59,6 +59,8 @@ int save_file(struct DrawState* state); struct DrawState* insert_line(struct DrawState* state); struct DrawState* join_lines(struct DrawState* state, size_t first_line, int forward); +int toggle_selection(struct DrawState* state); +int clear_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); @@ -111,11 +113,13 @@ format_mode(char* result, size_t result_size, const char* format, mode_label = mode_labels[HELP_MODE]; else if (state->in_prompt) mode_label = mode_labels[PROMPT_MODE]; + else if (state->in_selection_mode) + mode_label = mode_labels[SELECTION_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); } @@ -332,6 +336,11 @@ move_up(struct DrawState* state) = state->display_column - state->maxx + 1; state->display_column = display_length(state, state->buffer[state->row].text, state->column); + if (state->in_selection_mode) + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } return 0; } @@ -359,6 +368,11 @@ move_down(struct DrawState* state) = state->display_column - state->maxx + 1; state->display_column = display_length(state, state->buffer[state->row].text, state->column); + if (state->in_selection_mode) + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } return 0; } @@ -367,6 +381,11 @@ move_top(struct DrawState* state) { state->row = 0; state->first_shown_row = 0; + if (state->in_selection_mode) + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } return 0; } @@ -376,6 +395,11 @@ move_start(struct DrawState* state) state->column = 0; state->display_column = 0; state->first_shown_column = 0; + if (state->in_selection_mode) + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } return 0; } @@ -394,6 +418,11 @@ move_left(struct DrawState* state) if (state->display_column < state->first_shown_column) state->first_shown_column = state->display_column; + if (state->in_selection_mode) + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } return 0; } @@ -414,6 +443,11 @@ move_right(struct DrawState* state) if (state->display_column - state->first_shown_column > state->maxx - 1) state->first_shown_column = state->display_column - state->maxx + 1; + if (state->in_selection_mode) + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } return 0; } @@ -436,6 +470,11 @@ move_prev_word(struct DrawState* state) state->buffer[state->row].text, state->column); if (state->display_column < state->first_shown_column) state->first_shown_column = state->display_column; + if (state->in_selection_mode) + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } return 0; } @@ -456,6 +495,11 @@ move_next_word(struct DrawState* state) if (state->display_column - state->first_shown_column > state->maxx - 1) state->first_shown_column = state->display_column - state->maxx + 1; + if (state->in_selection_mode) + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } return 0; } @@ -504,6 +548,11 @@ 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; + if (state->in_selection_mode) + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } return 0; } @@ -521,6 +570,11 @@ move_end(struct DrawState* state) state->first_shown_column = dlen - state->maxx + 4; else state->first_shown_column = 0; + if (state->in_selection_mode) + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } return 0; } @@ -536,6 +590,11 @@ move_page_up(struct DrawState* state) } else move_top(state); + if (state->in_selection_mode) + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } return 0; } @@ -551,6 +610,11 @@ move_page_down(struct DrawState* state) } else move_bottom(state); + if (state->in_selection_mode) + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } return 0; } @@ -573,17 +637,14 @@ insert_char(struct DrawState* state, uint32_t ch) 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->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; + = state->display_column - state->maxx + 1; state->display_column = display_length(state, - state->buffer[state->row].text, - state->column); + state->buffer[state->row].text, state->column); } if (add_null) buffer_line->text[state->column] = 0; @@ -595,12 +656,16 @@ insert_char(struct DrawState* state, uint32_t ch) int insert_space(struct DrawState* state) { + // TODO: Replace selection + clear_selection(state); return insert_char(state, ' '); } int insert_tab(struct DrawState* state) { + // TODO: Replace selection + clear_selection(state); return insert_char(state, '\t'); } @@ -630,12 +695,9 @@ erase_backwards(struct DrawState* state) state->first_shown_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->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->row > 0) @@ -683,12 +745,11 @@ erase_to_start(struct DrawState* state) size_t len = buffer_line->length; do { - buffer_line->text[i - state->column] - = buffer_line->text[i]; + buffer_line->text[i - state->column] = buffer_line->text[i]; i++; } while (buffer_line->text[i]); buffer_line->text[len - state->column] = 0; - buffer_line->length = len - state->column; + buffer_line->length = len - state->column; move_start(state); state->dirty = 1; return 0; @@ -697,9 +758,9 @@ erase_to_start(struct DrawState* state) int erase_to_end(struct DrawState* state) { - struct BufferLine* buffer_line = &state->buffer[state->row]; + struct BufferLine* buffer_line = &state->buffer[state->row]; buffer_line->text[state->column] = 0; - buffer_line->length = state->column; + buffer_line->length = state->column; move_end(state); state->dirty = 1; return 0; @@ -720,27 +781,24 @@ insert_line(struct DrawState* state) if (!newtext) return NULL; state->buffer[state->rows_count - 1].text = newtext; - for (size_t i = state->rows_count - 1; i > state->row + 1; - i--) + for (size_t i = state->rows_count - 1; i > state->row + 1; i--) { - u32_strncpy(state->buffer[i].text, - state->buffer[i - 1].text, MAXBUFLINE); - state->buffer[i].length - = state->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->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, 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; + > 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; @@ -752,10 +810,9 @@ join_lines(struct DrawState* state, size_t first_line, int forward) if (!state) return NULL; - state->column = state->buffer[first_line].length; + state->column = state->buffer[first_line].length; state->display_column = display_length(state, - state->buffer[state->row].text, - state->column); + 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 @@ -768,10 +825,9 @@ join_lines(struct DrawState* state, size_t first_line, int forward) } for (size_t i = first_line + 1; i < state->rows_count - 1; i++) { - u32_strncpy(state->buffer[i].text, - state->buffer[i + 1].text, MAXBUFLINE); - state->buffer[i].length - = state->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->buffer + state->rows_count - 1); state->rows_count--; @@ -780,7 +836,7 @@ join_lines(struct DrawState* state, size_t first_line, int forward) if (!newbuf) return NULL; state->buffer = newbuf; - state->dirty = 1; + state->dirty = 1; return state; } @@ -790,16 +846,18 @@ cancel_close(struct DrawState* state) if (!state) return 1; - if (!*state->error && !state->in_prompt && !state->show_help) + if (!*state->error && !state->in_prompt && !state->show_help + && !state->in_selection_mode) strcpy(state->error, errors[ERR_EXIT_KEY]); - else - { - *state->error = 0; + else + { + *state->error = 0; *state->prompt = 0; state->in_prompt = 0; + clear_selection(state); state->show_help = 0; } - + return 0; } @@ -844,6 +902,38 @@ show_help(struct DrawState* state) return 0; } +int +toggle_selection(struct DrawState* state) +{ + state->in_selection_mode = !state->in_selection_mode; + if (state->in_selection_mode) + { + state->sel_start_row = state->row; + state->sel_start_col = state->column; + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } + else + { + state->sel_end_row = state->row; + state->sel_end_col = state->column; + } + return 0; +} + +int +clear_selection(struct DrawState* state) +{ + if (!state) + return 1; + state->in_selection_mode = 0; + state->sel_start_row = 0; + state->sel_start_col = 0; + state->sel_end_row = 0; + state->sel_end_col = 0; + return 0; +} + void handle_editbox_key_event(struct tb_event* ev, struct DrawState* state) { @@ -911,6 +1001,7 @@ handle_editbox_key_event(struct tb_event* ev, struct DrawState* state) void handle_key_event(struct tb_event* ev, struct DrawState* state) { + const struct Key* current = NULL; if (!state) return; @@ -930,13 +1021,13 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) 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)) + { + current = &top_level_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 (state->in_prompt) return; @@ -948,37 +1039,43 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) } 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)) + { + current = &global_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 (state->in_command_mode) { 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)) + { + current = &command_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; + } } 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)) + { + current = &insert_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) { + // TODO: Replace selection + clear_selection(state); insert_char(state, ev->ch); return; } @@ -1081,24 +1178,17 @@ handle_search_key_event(struct tb_event* ev, struct DrawState* state) 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) + 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); + 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); */ } @@ -1162,8 +1252,12 @@ main(int argc, char** argv) } tb_init(); - *error = 0; - state.maxx = 0; + *error = 0; + state.maxx = 0; + state.sel_start_row = 1; + state.sel_start_col = 3; + state.sel_end_row = 1; + state.sel_end_col = 6; while (state.running) { diff --git a/util.h b/util.h @@ -10,7 +10,8 @@ enum { COMMAND_MODE, HELP_MODE, INSERT_MODE, - PROMPT_MODE + PROMPT_MODE, + SELECTION_MODE }; enum { @@ -34,7 +35,8 @@ enum { #define MSGDELTA 1024 #define MAXPATH 1024 /* maximum chars for UTF-8 representations of Unicode chars, per Unicode char */ -#define UTF8REPMAX 6 +#define UTF8REPMAX 6 +#define TB_KEY_CTRL_SPACE 0x00 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);