commit a022230da56d11e366e2474e157572d23f08283e from: Marcel Rodrigues date: Sun Apr 26 22:15:02 2015 UTC Refactor line editing to allow insertion. commit - ddca21290abf5f18a0ece0af679320c330c70e74 commit + a022230da56d11e366e2474e157572d23f08283e blob - edf0805ef8935bc3798b78485cf849196e8f7a62 blob + 7b170d7ebe56cfb069638fef287a9c2de0310d5e --- rover.1 +++ rover.1 @@ -91,7 +91,7 @@ Open \fB$PAGER\fR with the selected file. Open \fB$EDITOR\fR with the selected file. .TP .B / -Start incremental search (\fB\fR to finish). +Start incremental search. .TP .B f/d/s Toggle file/directory/hidden listing. @@ -116,6 +116,34 @@ Delete/copy/move all marked entries. .TP .B 0-9 Change tab. +.SH LINE EDITING +.PP +Some commands will prompt for an input string. For example, in order to rename a +file, the user must supply the new name. This string will appear at the bottom +of the screen and must be edited interactivelly using the keyboard. Printable +keys will insert characters at the cursor position. The following shortcuts are +available for line editing: +.TP +.B +Finish editing and \fBcancel\fR command. +.TP +.B +Finish editing and \fBconfirm\fR command. +.TP +.B / +Move insertion cursor left/right. +.TP +.B / +Move insertion cursor to beginning/end of string. +.TP +.B +Remove one character before cursor. +.TP +.B +Remove one character after cursor. +.TP +.B +u +Clear line (remove all characters). .SH ENVIRONMENT VARIABLES .TP .B HOME blob - efacfe9d63b20d419939d7de80dc3c8e6760f29c blob + 5ea6fb141975a53f97fc32fb7d362b31b0b68dc2 --- rover.c +++ rover.c @@ -24,6 +24,7 @@ static char ROW[ROWSZ]; static char STATUS[STATUSSZ]; #define INPUTSZ 256 static char INPUT[INPUTSZ]; +#define EDITSZ 256 /* Argument buffers for execvp(). */ #define MAXARGS 256 @@ -57,6 +58,12 @@ typedef struct Marks { char **entries; } Marks; +/* Line editing state. */ +typedef struct Edit { + char buffer[EDITSZ]; + int left, right; +} Edit; + /* Global state. Some basic info is allocated for ten tabs. */ static struct Rover { int tab; @@ -68,6 +75,7 @@ static struct Rover { WINDOW *window; char cwd[10][PATH_MAX]; Marks marks; + Edit edit; } rover; /* Macros for accessing global state. */ @@ -83,7 +91,19 @@ static struct Rover { #define MIN(A, B) ((A) < (B) ? (A) : (B)) #define MAX(A, B) ((A) > (B) ? (A) : (B)) #define ISDIR(E) (strchr((E), '/') != NULL) + +/* Line Editing Macros. */ +#define EDIT_FULL(E) ((E).left > (E).right) +#define EDIT_CAN_LEFT(E) ((E).left) +#define EDIT_CAN_RIGHT(E) ((E).right < EDITSZ-1) +#define EDIT_LEFT(E) (E).buffer[(E).right--] = (E).buffer[--(E).left] +#define EDIT_RIGHT(E) (E).buffer[(E).left++] = (E).buffer[++(E).right] +#define EDIT_INSERT(E, C) (E).buffer[(E).left++] = (C) +#define EDIT_BACKSPACE(E) (E).left-- +#define EDIT_DELETE(E) (E).right++ +#define EDIT_CLEAR(E) do { (E).left = 0; (E).right = EDITSZ-1; } while(0) +typedef enum EditStat {CONTINUE, CONFIRM, CANCEL} EditStat; typedef enum Color {DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, MAGENTA, WHITE} Color; typedef int (*PROCESS)(const char *path); @@ -653,29 +673,51 @@ static int movfile(const char *srcpath) { return ret; } -/* Interactive getstr(). */ -static int -igetstr(char *buffer, int maxlen) +static void +start_line_edit(const char *init_input) { - int ch, length; - - length = strlen(buffer); curs_set(TRUE); - ch = getch(); - if (ch == '\r' || ch == '\n' || ch == KEY_DOWN || ch == KEY_ENTER) { + strncpy(INPUT, init_input, INPUTSZ); + strncpy(rover.edit.buffer, init_input, EDITSZ); + rover.edit.left = strlen(init_input); + rover.edit.right = EDITSZ - 1; +} + +/* Read input and change editing state accordingly. */ +static EditStat +get_line_edit() +{ + int ch = getch(); + if (ch == '\r' || ch == '\n' || ch == KEY_ENTER) { curs_set(FALSE); - return 0; - } else if (ch == erasechar() || ch == KEY_LEFT || ch == KEY_BACKSPACE) { - if (length) - buffer[--length] = '\0'; - } else if (ch == killchar()) { - length = 0; - buffer[0] = '\0'; - } else if (length < maxlen - 1 && isprint(ch)) { - buffer[length++] = ch; - buffer[length] = '\0'; + return CONFIRM; + } else if (ch == '\t') { + curs_set(FALSE); + return CANCEL; + } else if (EDIT_CAN_LEFT(rover.edit) && ch == KEY_LEFT) { + EDIT_LEFT(rover.edit); + } else if (EDIT_CAN_RIGHT(rover.edit) && ch == KEY_RIGHT) { + EDIT_RIGHT(rover.edit); + } else if (ch == KEY_UP) { + while (EDIT_CAN_LEFT(rover.edit)) EDIT_LEFT(rover.edit); + } else if (ch == KEY_DOWN) { + while (EDIT_CAN_RIGHT(rover.edit)) EDIT_RIGHT(rover.edit); + } else if (ch == erasechar() || ch == KEY_BACKSPACE) { + if (EDIT_CAN_LEFT(rover.edit)) EDIT_BACKSPACE(rover.edit); + } else if (ch == KEY_DC) { + if (EDIT_CAN_RIGHT(rover.edit)) EDIT_DELETE(rover.edit); + } else if (ch == killchar()) { + EDIT_CLEAR(rover.edit); + clear_message(); + } else if (!EDIT_FULL(rover.edit) && isprint(ch)) { + EDIT_INSERT(rover.edit, ch); } - return 1; + /* Copy edit contents to INPUT and append null character. */ + strncpy(INPUT, rover.edit.buffer, MIN(rover.edit.left, INPUTSZ-1)); + strncpy(&INPUT[rover.edit.left], &rover.edit.buffer[rover.edit.right+1], + MIN(EDITSZ-rover.edit.right-1, INPUTSZ-rover.edit.left-1)); + INPUT[MIN(rover.edit.left+EDITSZ-rover.edit.right-1, INPUTSZ-1)] = '\0'; + return CONTINUE; } /* Update line input on the screen. */ @@ -690,8 +732,8 @@ update_input(char *prompt, Color color) mvaddstr(LINES - 1, 0, prompt); color_set(color, NULL); mvaddstr(LINES - 1, plen, INPUT); - mvaddch(LINES - 1, ilen + plen, ' '); - move(LINES - 1, ilen + plen); + mvaddch(LINES - 1, plen + ilen, ' '); + move(LINES - 1, plen + rover.edit.left); color_set(DEFAULT, NULL); } @@ -702,6 +744,7 @@ main(int argc, char *argv[]) char *program; const char *key; DIR *d; + EditStat edit_stat; if (argc == 2) { if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")) { @@ -832,9 +875,9 @@ main(int argc, char *argv[]) if (!rover.nfiles) continue; oldsel = ESEL; oldscroll = SCROLL; - strcpy(INPUT, ""); + start_line_edit(""); update_input(prompt, DEFAULT); - while (igetstr(INPUT, INPUTSZ)) { + while ((edit_stat = get_line_edit()) == CONTINUE) { int sel; Color color = RED; length = strlen(INPUT); @@ -861,6 +904,10 @@ main(int argc, char *argv[]) update_view(); update_input(prompt, color); } + if (edit_stat == CANCEL) { + ESEL = oldsel; + SCROLL = oldscroll; + } clear_message(); update_view(); } else if (!strcmp(key, RVK_TG_FILES)) { @@ -875,9 +922,9 @@ main(int argc, char *argv[]) } else if (!strcmp(key, RVK_NEW_FILE)) { int ok = 0; char *prompt = "new file: "; - strcpy(INPUT, ""); + start_line_edit(""); update_input(prompt, DEFAULT); - while (igetstr(INPUT, INPUTSZ)) { + while ((edit_stat = get_line_edit()) == CONTINUE) { int length = strlen(INPUT); ok = 1; for (i = 0; i < rover.nfiles; i++) { @@ -893,7 +940,7 @@ main(int argc, char *argv[]) update_input(prompt, ok ? GREEN : RED); } clear_message(); - if (strlen(INPUT)) { + if (edit_stat == CONFIRM && strlen(INPUT)) { if (ok) { addfile(INPUT); cd(1); @@ -905,9 +952,9 @@ main(int argc, char *argv[]) } else if (!strcmp(key, RVK_NEW_DIR)) { int ok = 0; char *prompt = "new directory: "; - strcpy(INPUT, ""); + start_line_edit(""); update_input(prompt, DEFAULT); - while (igetstr(INPUT, INPUTSZ)) { + while ((edit_stat = get_line_edit()) == CONTINUE) { int length = strlen(INPUT); ok = 1; for (i = 0; i < rover.nfiles; i++) { @@ -923,7 +970,7 @@ main(int argc, char *argv[]) update_input(prompt, ok ? GREEN : RED); } clear_message(); - if (strlen(INPUT)) { + if (edit_stat == CONFIRM && strlen(INPUT)) { if (ok) { adddir(INPUT); cd(1); @@ -941,8 +988,9 @@ main(int argc, char *argv[]) last = INPUT + strlen(INPUT) - 1; if ((isdir = *last == '/')) *last = '\0'; + start_line_edit(INPUT); update_input(prompt, RED); - while (igetstr(INPUT, INPUTSZ)) { + while ((edit_stat = get_line_edit()) == CONTINUE) { int length = strlen(INPUT); ok = 1; for (i = 0; i < rover.nfiles; i++) @@ -957,7 +1005,7 @@ main(int argc, char *argv[]) update_input(prompt, ok ? GREEN : RED); } clear_message(); - if (strlen(INPUT)) { + if (edit_stat == CONFIRM && strlen(INPUT)) { if (isdir) strcat(INPUT, "/"); if (ok) {