add undo support for box, visual, cut/paste - gramscii - A simple editor for ASCII box-and-arrow charts DIR Log DIR Files DIR Refs DIR Tags DIR README DIR LICENSE --- DIR commit f3100ddd486d6f39b0c97c9c492bb6020bf3caf1 DIR parent 526ce3a130732d4a2374a6e36a689d9e0cf5cc34 HTML Author: KatolaZ <katolaz@freaknet.org> Date: Wed, 31 Jul 2019 11:19:55 +0100 add undo support for box, visual, cut/paste Diffstat: M TODO | 4 ++++ M draw.c | 32 +++++++++++++++++-------------- M gramscii.1 | 24 ++++++++++++++++++++++-- M gramscii.h | 4 ++-- M lineset.c | 38 +++++++++++++++++-------------- M screen.c | 4 ++-- 6 files changed, 69 insertions(+), 37 deletions(-) --- DIR diff --git a/TODO b/TODO @@ -1,5 +1,9 @@ + optimize redraws (redraw only the modified rectangle) + undo (by storing lines changed across insert/remove operations) + * re-organise undo-ring management + - add undo for arrow mode + - add undo for text mode + - add undo for erase mode - fix bug with 'g' commands in arrow mode - add screen geometry option (-g 25x80?) - read file at point DIR diff --git a/draw.c b/draw.c @@ -112,7 +112,7 @@ void draw_box(int x1, int y1, int fix){ if (fix == FIX){ f = set_xy; - copy_lines_to_ring(ymin, ymax, CUR); + copy_lines_to_ring(ymin, ymax, PRV_STATE); } else f = draw_xy; @@ -130,7 +130,7 @@ void draw_box(int x1, int y1, int fix){ f(xmax, ymin, corner); f(xmax, ymax, corner); if (fix == FIX) - copy_lines_to_ring(ymin, ymax, LST); + copy_lines_to_ring(ymin, ymax, NEW_STATE); show_cursor(); } @@ -316,10 +316,10 @@ void visual_box(FILE *fc){ case 'x':/* erase */ if (c == 'x') yank_region(MIN(orig_x,x), MIN(orig_y,y), MAX(orig_x, x), MAX(orig_y, y)); - copy_lines_to_ring(MIN(orig_y, y), MAX(orig_y, y), CUR); + copy_lines_to_ring(MIN(orig_y, y), MAX(orig_y, y), PRV_STATE); erase_box(orig_x, orig_y, f); erase_blank_lines(MIN(y,orig_y), MAX(y, orig_y)); - copy_lines_to_ring(MIN(orig_y, y), MAX(orig_y, y), LST); + copy_lines_to_ring(MIN(orig_y, y), MAX(orig_y, y), NEW_STATE); modified = 1; goto vis_exit; @@ -345,9 +345,9 @@ void paste(){ int y2; y2 = y + cutbuf.num - 1; - copy_lines_to_ring(y, y2, CUR); + copy_lines_to_ring(y, y2, PRV_STATE); paste_region(x, y); - copy_lines_to_ring(y, y2, LST); + copy_lines_to_ring(y, y2, NEW_STATE); redraw(); } @@ -356,28 +356,32 @@ void put_lines(lineset_t *u){ for (i=0; i< u->num; i++){ n = u->l[i].n; - ensure_line_length(&(screen.l[i]), u->l[i].lst); + ensure_line_length(&(screen.l[i]), strlen(u->l[i].s)); strcpy(screen.l[n].s, u->l[i].s); - screen.l[n].lst = u->l[i].lst; + screen.l[n].lst = strlen(u->l[i].s)-1; } } void undo_change(){ if (undo_cur >= 0){ + if (undo_cur > undo_lst) + undo_cur = undo_lst; put_lines(& (undo[undo_cur])); - undo_cur --; + undo_cur -= 2; + modified = 1; } redraw(); - modified = 1; } void redo_change(){ - if (undo_cur < undo_lst){ - undo_cur ++; - put_lines(& (undo[undo_cur])); + if (undo_cur <= undo_lst-2){ + if (undo_cur > 0) + put_lines(& (undo[undo_cur+1])); + undo_cur +=2; + put_lines(& (undo[undo_cur+1])); + modified = 1; } redraw(); - modified = 1; } DIR diff --git a/gramscii.1 b/gramscii.1 @@ -63,6 +63,26 @@ buffer contains the rectangle yanked/cut in .B visual mode. .TP 5m +.BI u +Undo the last change. gramscii supports an undo history of indefinite +length. The command +.BI u +gets the last change from the history, and moves the history pointer +back by one change. See the related command +.BI U +below. +.TP 5m +.BI U +Redo, i.e., cancel a previous +.BI u +command. gramscii supports an undo history of indefinite length. The +command +.BI U +moves to the following change, if possible. For instance, the sequence +.BI uuU +will go back two changes, and then forward one, effectively resetting +the state of the screen to what it was before the last change occurred. +.TP 5m .BI q Quit gramscii, and prompt for a filename if the current screen contains unsaved changes. @@ -516,8 +536,8 @@ support scrolling and "virtual" screens of any (reasonable) size. .PP gramscii currently does .B not -provide an "undo" command. This requires a restructuring of buffer -management, and will most probably be implemented in a future release. +support "undo" commands for arrow, text, and erase mode. This will be +added soon. .SH AUTHORS gramscii is written and maintained by Vincenzo "KatolaZ" Nicosia <katolaz@freaknet.org>. You can use, copy, modify, and redistribute DIR diff --git a/gramscii.h b/gramscii.h @@ -44,8 +44,8 @@ #define VIDEO_NRM 0 #define VIDEO_REV 7 -#define CUR 0x01 -#define LST 0x02 +#define PRV_STATE 0x01 +#define NEW_STATE 0x02 /** types **/ DIR diff --git a/lineset.c b/lineset.c @@ -127,19 +127,24 @@ void paste_region(int x1, int y1){ } void copy_lines_to_ring(int y1, int y2, int which){ - lineset_t *tmp; - int i, len, *idx; + int i, len, idx; + int offset; + lineset_t *tmp; if (y1 > y2){ y1 ^= y2; y2 ^= y1; y1 ^= y2; } - if (which == CUR) - idx = &undo_cur; + if (undo_cur > undo_lst) + undo_cur = undo_lst; + if (which == PRV_STATE){ /* adding a new previous state */ + undo_cur += 2; + idx = undo_cur; + } else - idx = &undo_lst; - if (*idx == undo_sz - 1){ + idx = undo_cur + 1; + if (idx >= undo_sz - 1){ undo_sz += 10; tmp = realloc(undo, undo_sz * sizeof(lineset_t)); if (tmp == NULL){ @@ -148,22 +153,21 @@ void copy_lines_to_ring(int y1, int y2, int which){ } undo = tmp; } - (*idx) ++; - ensure_num_lines(&(undo[*idx]), y2 - y1 + 1); + ensure_num_lines(&(undo[idx]), y2 - y1 + 1); for(i=y1; i<=y2; i++){ len = strlen(screen.l[i].s); - ensure_line_length(&(undo[*idx].l[i-y1]), len); - strcpy(undo[*idx].l[i-y1].s, screen.l[i].s); - undo[*idx].l[i-y1].n = i; - undo[*idx].l[i-y1].lst = screen.l[i].lst; + ensure_line_length(&(undo[idx].l[i-y1]), len); + strcpy(undo[idx].l[i-y1].s, screen.l[i].s); + undo[idx].l[i-y1].n = i; + undo[idx].l[i-y1].lst = screen.l[i].lst; } - undo[*idx].num = y2 - y1 + 1; - if (which == CUR) + undo[idx].num = y2 - y1 + 1; + if (which == PRV_STATE) undo_lst = undo_cur; #ifdef DEBUG - fprintf(stderr, "undo_ring: y1: %d y2: %d idx: %d\n", y1, y2, *idx); - for(i=0; i<undo[undo_cur].num; i++){ - fprintf(stderr, "UU: %d| %s\n", undo[*idx].l[i].n, undo[*idx].l[i].s); + fprintf(stderr, "undo_ring: y1: %d y2: %d idx: %d\n", y1, y2, idx); + for(i=0; i<undo[idx].num; i++){ + fprintf(stderr, "UU: %d| %s\n", undo[idx].l[i].n, undo[idx].l[i].s); } #endif } DIR diff --git a/screen.c b/screen.c @@ -410,8 +410,8 @@ void init_screen(){ cutbuf.num = 0; undo_sz = 0; - undo_cur = -1; - undo_lst = -1; + undo_cur = -2; + undo_lst = -2; } void find_nonblank_rect(int *x1, int *y1, int *x2, int *y2){