From d71cad22b0ed3938db63f14ad01775cf2d85d94d Mon Sep 17 00:00:00 2001 From: Evan New-Schmidt Date: Thu, 28 Nov 2019 00:22:41 -0500 Subject: [PATCH 1/5] Fix comment typo --- src/frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend.c b/src/frontend.c index fca3b29..edaf21a 100644 --- a/src/frontend.c +++ b/src/frontend.c @@ -733,7 +733,7 @@ void update_info_win(const Mode_ID current_mode, const int x, const int y, /* Signal handler for exiting * - * Turns of ncurses, disables mouse moves commands, closes the logfile. + * Turns off ncurses, disables mouse moves commands, closes the logfile. * * If sig is 0 or SIGINT, exits normally, otherwise prints the signal * information to stderr and exits with sig. From f29c80d9cf348694775e8c2877edce683a1a9b0b Mon Sep 17 00:00:00 2001 From: Evan New-Schmidt Date: Thu, 28 Nov 2019 21:23:59 -0500 Subject: [PATCH 2/5] Somewhat-working goto functionality --- src/fe_modes.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/fe_modes.h | 2 + src/mode_id.h | 1 + 3 files changed, 102 insertions(+) diff --git a/src/fe_modes.c b/src/fe_modes.c index a8527e7..f00eda8 100644 --- a/src/fe_modes.c +++ b/src/fe_modes.c @@ -23,6 +23,7 @@ #include "fe_modes.h" +#include #include #include @@ -39,6 +40,8 @@ editor_mode_t modes[] = { {"Pan", "Pan around the canvas", mode_pan}, {"Free-Line", "Draw a line with your arrow keys", mode_free_line}, {"Brush", "Paint with arrow keys and mouse", mode_brush}, + {"", "", NULL}, + {"GOTO", "", mode_goto}, }; typedef struct { @@ -57,6 +60,14 @@ typedef struct { mode_insert_config_t mode_insert_config = {NULL}; +typedef struct { + enum { ENTER_FIRST, ENTER_SECOND } state; + char buffer[8]; + int xpos, ypos; +} mode_goto_config_t; + +mode_goto_config_t mode_goto_config = {ENTER_FIRST, "", 0, 0}; + /////////////////////// // GENERAL FUNCTIONS // /////////////////////// @@ -226,6 +237,8 @@ int master_handler(State *state, WINDOW *canvas_win, WINDOW *status_win) { print_msg_win("Saved to file '%s'\n", state->filepath); } else if (c == KEY_CTRL('t')) { cmd_trim_canvas(state); + } else if (c == KEY_CTRL('g')) { + switch_mode(MODE_GOTO, state); } else { // pass character on to mode state->ch_in = c; @@ -533,3 +546,89 @@ int mode_brush(reason_t reason, State *state) { return 0; } + +int update_pos_value(mode_goto_config_t *mode_cfg) { + int *pos; + switch (mode_cfg->state) { + case ENTER_FIRST: + pos = &(mode_cfg->xpos); + break; + case ENTER_SECOND: + pos = &(mode_cfg->ypos); + } + char *buff = mode_cfg->buffer; + // parse buffer value + errno = 0; + int val = (int)strtol(buff, NULL, 10); + if (errno != 0) { + perror("update_pos_value strtol"); + print_mode_win("Invalid number"); + return 1; + } + // move relative if sign is present + switch (buff[0]) { + case '-': + case '+': + *pos = *pos + val; + break; + default: + *pos = val; + break; + } + return 0; +} + +/* mode_goto + * + * Move the cursor to a position entered on the keyboard. + */ +int mode_goto(reason_t reason, State *state) { + mode_goto_config_t *mode_cfg = &mode_goto_config; + char *buff = mode_cfg->buffer; + if (reason == START) { + // reset mode state + mode_cfg->state = ENTER_FIRST; + mode_cfg->xpos = state->view->x + state->cursor->x; + mode_cfg->ypos = state->view->y + state->cursor->y; + memset(buff, 0, 8); + } + if (reason != NEW_KEY) { + return 0; + } + + switch (state->ch_in) { + case ',': + // parse, switch to second value + update_pos_value(mode_cfg); + memset(buff, 0, 8); // reset buffer + mode_cfg->state = ENTER_SECOND; + break; + case KEY_ENTER: + // parse, set cursor location and return to last mode + update_pos_value(mode_cfg); + memset(buff, 0, 8); // reset buffer + state->cursor->x = mode_cfg->xpos - state->view->x; + state->cursor->y = mode_cfg->ypos - state->view->y; + // TODO: return somehow + break; + case KEY_BACKSPACE: { + // remove last char in buffer + int l = strlen(buff); + if (l > 0) { + buff[l] = '\0'; + } + } + default: { + // check if valid and add to buffer + // TODO: check for validity + int l = strlen(buff); + if (l < 8 - 1) { + buff[l] = state->ch_in; + } + break; + } + } + + print_mode_win("Go to pos: (%i, %i)", mode_cfg->xpos, mode_cfg->ypos); + return 0; +} diff --git a/src/fe_modes.h b/src/fe_modes.h index 39f0b0e..bae421f 100644 --- a/src/fe_modes.h +++ b/src/fe_modes.h @@ -18,6 +18,8 @@ mode_function_t mode_pan; mode_function_t mode_free_line; mode_function_t mode_brush; +mode_function_t mode_goto; + typedef struct { char *name; char *description; diff --git a/src/mode_id.h b/src/mode_id.h index 98d806c..6df583c 100644 --- a/src/mode_id.h +++ b/src/mode_id.h @@ -15,6 +15,7 @@ typedef enum { // ^ add your mode above LAST, // used to get number of elements + MODE_GOTO, } Mode_ID; #endif From 1e573832431d56de2c4d38e05ae185fbdc61f197 Mon Sep 17 00:00:00 2001 From: Evan New-Schmidt Date: Thu, 28 Nov 2019 21:24:47 -0500 Subject: [PATCH 3/5] Add response codes --- src/fe_modes.c | 66 +++++++++++++++++++++++++------------------------- src/fe_modes.h | 5 ++++ 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/fe_modes.c b/src/fe_modes.c index f00eda8..209bf3f 100644 --- a/src/fe_modes.c +++ b/src/fe_modes.c @@ -108,6 +108,9 @@ void cmd_trim_canvas(State *state) { int call_mode(Mode_ID mode, reason_t reason, State *state) { int res = modes[mode].mode_function(reason, state); update_info_win_state(state); + if (res & RESP_EXIT) { + switch_mode(state->last_canvas_mode, state); + } return res; } @@ -591,41 +594,38 @@ int mode_goto(reason_t reason, State *state) { mode_cfg->xpos = state->view->x + state->cursor->x; mode_cfg->ypos = state->view->y + state->cursor->y; memset(buff, 0, 8); - } - if (reason != NEW_KEY) { - return 0; - } - - switch (state->ch_in) { - case ',': - // parse, switch to second value - update_pos_value(mode_cfg); - memset(buff, 0, 8); // reset buffer - mode_cfg->state = ENTER_SECOND; - break; - case KEY_ENTER: - // parse, set cursor location and return to last mode - update_pos_value(mode_cfg); - memset(buff, 0, 8); // reset buffer - state->cursor->x = mode_cfg->xpos - state->view->x; - state->cursor->y = mode_cfg->ypos - state->view->y; - // TODO: return somehow - break; - case KEY_BACKSPACE: { - // remove last char in buffer - int l = strlen(buff); - if (l > 0) { - buff[l] = '\0'; + } else if (reason == NEW_KEY) { + switch (state->ch_in) { + case ',': + // parse, switch to second value + update_pos_value(mode_cfg); + memset(buff, 0, 8); // reset buffer + mode_cfg->state = ENTER_SECOND; + break; + case KEY_ENTER: + // parse, set cursor location and return to last mode + update_pos_value(mode_cfg); + memset(buff, 0, 8); // reset buffer + state->cursor->x = mode_cfg->xpos - state->view->x; + state->cursor->y = mode_cfg->ypos - state->view->y; + // TODO: return somehow + return RESP_EXIT; + case KEY_BACKSPACE: { + // remove last char in buffer + int l = strlen(buff); + if (l > 0) { + buff[l] = '\0'; + } } - } - default: { - // check if valid and add to buffer - // TODO: check for validity - int l = strlen(buff); - if (l < 8 - 1) { - buff[l] = state->ch_in; + default: { + // check if valid and add to buffer + // TODO: check for validity + int l = strlen(buff); + if (l < 8 - 1) { + buff[l] = state->ch_in; + } + break; } - break; } } diff --git a/src/fe_modes.h b/src/fe_modes.h index bae421f..6f80ad4 100644 --- a/src/fe_modes.h +++ b/src/fe_modes.h @@ -8,6 +8,11 @@ int mode_master(State *state, WINDOW *canvas_win, WINDOW *status_win); typedef enum { START, NEW_KEY, NEW_MOUSE, END } reason_t; +// mode responses +#define RESP_OK 0 +#define RESP_EXIT 1 +#define RESP_REDRAW 2 + // Prototype for mode functions. Note that this is NOT a function pointer type // (use `mode_function_t*` for that). https://stackoverflow.com/a/5195682 typedef int mode_function_t(reason_t, State *); From efd44e6b95a250d56bcb5901f10d04084ebd4620 Mon Sep 17 00:00:00 2001 From: Evan New-Schmidt Date: Thu, 28 Nov 2019 23:02:12 -0500 Subject: [PATCH 4/5] Restructure modes for meta-modes --- src/fe_modes.c | 10 ++++++---- src/frontend.c | 1 + src/mode_id.h | 5 +++-- src/state.h | 1 + 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/fe_modes.c b/src/fe_modes.c index 209bf3f..2f8b6f8 100644 --- a/src/fe_modes.c +++ b/src/fe_modes.c @@ -35,13 +35,15 @@ // #define LOG_KEY_EVENTS // `logd` new mouse and key events editor_mode_t modes[] = { + {"", "", NULL}, // empty (0) {"Switcher", "Switch to another mode", mode_picker}, + {"GOTO", "", mode_goto}, + {"", "", NULL}, // USER_MODES {"Insert", "Insert characters", mode_insert}, {"Pan", "Pan around the canvas", mode_pan}, {"Free-Line", "Draw a line with your arrow keys", mode_free_line}, {"Brush", "Paint with arrow keys and mouse", mode_brush}, - {"", "", NULL}, - {"GOTO", "", mode_goto}, + {"", "", NULL}, // END }; typedef struct { @@ -312,8 +314,8 @@ int mode_picker(reason_t reason, State *state) { } // get bounds of switchable modes in enum array (DON'T include mode_picker) - int mode_first = MODE_PICKER + 1; // beginning of selectable modes - int mode_list_end = LAST; // length of total mode list + int mode_first = USER_MODES + 1; // beginning of selectable modes + int mode_list_end = LAST; // length of total mode list // BUILD MODE INFO MESSAGE char msg[128] = ""; diff --git a/src/frontend.c b/src/frontend.c index edaf21a..3ca0199 100644 --- a/src/frontend.c +++ b/src/frontend.c @@ -304,6 +304,7 @@ void init_state(State *state, const arguments_t *const arguments) { .last_arrow_direction = KEY_RIGHT, .last_canvas_mode = MODE_INSERT, + .override_mode = 0, .view = view, .last_cursor = cursor_newyx(arguments->y, arguments->x), .filepath = arguments->filename, diff --git a/src/mode_id.h b/src/mode_id.h index 6df583c..310112b 100644 --- a/src/mode_id.h +++ b/src/mode_id.h @@ -7,7 +7,9 @@ * This enum is used to index into the modes array. */ typedef enum { - MODE_PICKER, + MODE_PICKER = 1, + MODE_GOTO, + USER_MODES, // empty start of user modes MODE_INSERT, MODE_PAN, MODE_FREE_LINE, @@ -15,7 +17,6 @@ typedef enum { // ^ add your mode above LAST, // used to get number of elements - MODE_GOTO, } Mode_ID; #endif diff --git a/src/state.h b/src/state.h index b4c9d25..ee47b67 100644 --- a/src/state.h +++ b/src/state.h @@ -19,6 +19,7 @@ typedef struct { View *view; // view of the canvas (also holds the canvas) Mode_ID current_mode; // the current mode of the interface Mode_ID last_canvas_mode; // the last mode of the interface + Mode_ID override_mode; // any special mode that should be used instead int last_arrow_direction; Cursor *last_cursor; char *filepath; // path of savefile From a7125fe9e19beb4db096b7714763b7fc9f8f40aa Mon Sep 17 00:00:00 2001 From: Evan New-Schmidt Date: Fri, 29 Nov 2019 15:49:57 -0500 Subject: [PATCH 5/5] Working override modes --- src/fe_modes.c | 113 ++++++++++++++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 43 deletions(-) diff --git a/src/fe_modes.c b/src/fe_modes.c index 2f8b6f8..ce30cde 100644 --- a/src/fe_modes.c +++ b/src/fe_modes.c @@ -128,7 +128,7 @@ inline void update_info_win_state(State *state) { * * It sends END to the old mode and then START to the new mode */ -void switch_mode(Mode_ID new_mode, State *state) { +inline void switch_mode(Mode_ID new_mode, State *state) { logd("Switching to %s\n", modes[new_mode].name); call_mode(state->current_mode, END, state); state->last_canvas_mode = state->current_mode; @@ -138,6 +138,32 @@ void switch_mode(Mode_ID new_mode, State *state) { refresh_screen(); } +// switch from normal mode operation to an overridden mode +void enter_override_mode(Mode_ID override_mode, State *state) { + state->override_mode = override_mode; + call_mode(state->override_mode, START, state); +} + +// return control from override mode to normal mode +void exit_override_mode(State *state) { + call_mode(state->override_mode, END, state); + state->override_mode = 0; + // TODO: "redraw" old mode +} + +// TODO: better name +void handle_mode(reason_t reason, State *state) { + if (state->override_mode != 0) { + int res = call_mode(state->override_mode, reason, state); + if (res & RESP_EXIT) { + logd("Exiting override mode %s\n", modes[state->override_mode].name); + exit_override_mode(state); + } + } else { + call_mode(state->current_mode, reason, state); + } +} + Mode_ID add_mod_canvas_mode(Mode_ID mode, int n) { int mode_first = MODE_PICKER + 1; // beginning of selectable modes int mode_list_end = LAST; // length of total mode list @@ -178,31 +204,17 @@ int master_handler(State *state, WINDOW *canvas_win, WINDOW *status_win) { // https://invisible-island.net/ncurses/man/curs_mouse.3x.html state->ch_in = c; state->mevent_in = &event; - call_mode(state->current_mode, NEW_MOUSE, state); - } - } else if (c == KEY_TAB) { // switching modes - if (state->current_mode == MODE_PICKER && - state->last_canvas_mode != MODE_PICKER) { - state->last_canvas_mode = next_canvas_mode(state->last_canvas_mode); - - // update mode_picker() to redraw the highlight - state->ch_in = c; - call_mode(state->current_mode, NEW_KEY, state); - } else { - switch_mode(MODE_PICKER, state); + handle_mode(NEW_MOUSE, state); } return 0; - } else if (c == KEY_SHIFT_TAB) { - if (state->current_mode == MODE_PICKER && - state->last_canvas_mode != MODE_PICKER) { - state->last_canvas_mode = previous_canvas_mode(state->last_canvas_mode); - - // update mode_picker() to redraw the highlight - state->ch_in = c; - call_mode(state->current_mode, NEW_KEY, state); - } else { - switch_mode(MODE_PICKER, state); + } else if (c == KEY_TAB) { // switch modes + if (state->override_mode != MODE_PICKER) { + enter_override_mode(MODE_PICKER, state); + return 0; } + } else if (c == KEY_CTRL('g')) { // goto pos + enter_override_mode(MODE_GOTO, state); + return 0; } else if (c == KEY_NPAGE || c == KEY_PPAGE || c == KEY_SLEFT || c == KEY_SRIGHT) { // shift view down/up/left/right @@ -234,21 +246,22 @@ int master_handler(State *state, WINDOW *canvas_win, WINDOW *status_win) { state->view->x = new_vx; redraw_canvas_win(); update_info_win_state(state); + return 0; } else if (c == KEY_CTRL('r')) { cmd_read_from_file(state); print_msg_win("Read from file '%s'\n", state->filepath); + return 0; } else if (c == KEY_CTRL('s')) { cmd_write_to_file(state); print_msg_win("Saved to file '%s'\n", state->filepath); + return 0; } else if (c == KEY_CTRL('t')) { cmd_trim_canvas(state); - } else if (c == KEY_CTRL('g')) { - switch_mode(MODE_GOTO, state); - } else { - // pass character on to mode - state->ch_in = c; - call_mode(state->current_mode, NEW_KEY, state); + return 0; } + // pass character on to mode + state->ch_in = c; + handle_mode(NEW_KEY, state); // Move UI cursor to the right place wmove(canvas_win, cursor_y_to_canvas(state->cursor), @@ -317,6 +330,34 @@ int mode_picker(reason_t reason, State *state) { int mode_first = USER_MODES + 1; // beginning of selectable modes int mode_list_end = LAST; // length of total mode list + // INTERPRET KEYS + Mode_ID new_mode = 0; + if (reason == NEW_KEY) { + // only accept characters within the bounds of the list + if (state->ch_in >= '1' && + state->ch_in < '1' + mode_list_end - mode_first) { + new_mode = mode_first + state->ch_in - '1'; + } else if (state->ch_in == KEY_ENTER) { + new_mode = state->last_canvas_mode; + } else if (state->ch_in == KEY_TAB) { + state->last_canvas_mode = next_canvas_mode(state->last_canvas_mode); + } else if (state->ch_in == KEY_SHIFT_TAB) { + state->last_canvas_mode = previous_canvas_mode(state->last_canvas_mode); + } + } + + // exit early if switching modes + if (new_mode != 0) { + // this is a little funky. We can't just return from override mode, because + // the new mode isn't initialized, but we can't switch without turning off + // override_mode + exit_override_mode(state); + switch_mode(new_mode, state); + return 0; + } + + // DRAW INTERFACE + // BUILD MODE INFO MESSAGE char msg[128] = ""; int num_left = sizeof(msg) / sizeof(char); @@ -355,20 +396,6 @@ int mode_picker(reason_t reason, State *state) { if (selected_num_char != -1) { highlight_mode_text(selected_x, selected_num_char); } - - // INTERPRET KEYS - if (reason == NEW_KEY) { - // only accept characters within the bounds of the list - if (state->ch_in >= '1' && - state->ch_in < '1' + mode_list_end - mode_first) { - Mode_ID new_mode = mode_first + state->ch_in - '1'; - switch_mode(new_mode, state); - return 0; - } else if (state->ch_in == KEY_ENTER) { - switch_mode(state->last_canvas_mode, state); - return 0; - } - } return 0; }