чување 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:
| M | TODO | | | 27 | ++++++++++++++------------- |
| M | config.def.h | | | 85 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- |
| M | config.h | | | 85 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- |
| M | draw.c | | | 124 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- |
| M | draw.h | | | 21 | ++++++++++++++++++--- |
| M | sled.1.in | | | 2 | ++ |
| M | sled.c | | | 463 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------- |
| M | util.c | | | 6 | +++--- |
| M | util.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);