preliminary support for undo - gramscii - A simple editor for ASCII box-and-arrow charts DIR Log DIR Files DIR Refs DIR Tags DIR README DIR LICENSE --- DIR commit 526ce3a130732d4a2374a6e36a689d9e0cf5cc34 DIR parent 6da2f3f89afda08eeba385da1c36414154113d47 HTML Author: KatolaZ <katolaz@freaknet.org> Date: Wed, 31 Jul 2019 00:10:35 +0100 preliminary support for undo Diffstat: M TODO | 2 +- M draw.c | 55 ++++++++++++++++++++++++++++--- M gramscii.h | 13 ++++++++++++- M lineset.c | 43 ++++++++++++++++++++++++++++++ M main.c | 6 ++++++ M screen.c | 4 ++++ 6 files changed, 117 insertions(+), 6 deletions(-) --- DIR diff --git a/TODO b/TODO @@ -1,4 +1,5 @@ + optimize redraws (redraw only the modified rectangle) ++ undo (by storing lines changed across insert/remove operations) - fix bug with 'g' commands in arrow mode - add screen geometry option (-g 25x80?) - read file at point @@ -18,7 +19,6 @@ * yank * fill * cut -- undo (by storing lines changed across insert/remove operations) - manage special chars (DEL/CANC) during text insert (also do not print unmanaged chars!) - allow scrolling (both vertical and horizontal) DIR diff --git a/draw.c b/draw.c @@ -1,4 +1,5 @@ #include <stdlib.h> +#include <string.h> #include "gramscii.h" #include "config.h" @@ -103,16 +104,19 @@ void draw_box(int x1, int y1, int fix){ int i; void (*f)(int, int, char); - if (fix == FIX) - f = set_xy; - else - f = draw_xy; xmin = MIN(x, x1); xmax = MAX(x, x1); ymin = MIN(y, y1); ymax = MAX(y, y1); + if (fix == FIX){ + f = set_xy; + copy_lines_to_ring(ymin, ymax, CUR); + } + else + f = draw_xy; + for(i=xmin+1; i<=xmax; i++){ f(i, ymin, line_h); f(i, ymax, line_h); @@ -125,6 +129,8 @@ void draw_box(int x1, int y1, int fix){ f(xmin, ymax, corner); f(xmax, ymin, corner); f(xmax, ymax, corner); + if (fix == FIX) + copy_lines_to_ring(ymin, ymax, LST); show_cursor(); } @@ -157,6 +163,7 @@ update_box: void draw_arrow(int x, int y, char *a, int a_len, int fix){ + /* FIXME: copy affected lines to undo */ int i, j, cur_dir; char line; void (*f)(int, int, char); @@ -266,6 +273,7 @@ void do_erase(int x1, int y1){ void erase(FILE *fc){ + /*FIXME: add affected lines to undo */ char c; int orig_x = x, orig_y = y; status_bar(); @@ -308,8 +316,11 @@ 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); 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); + modified = 1; goto vis_exit; break; @@ -331,6 +342,42 @@ vis_exit: } void paste(){ + int y2; + + y2 = y + cutbuf.num - 1; + copy_lines_to_ring(y, y2, CUR); paste_region(x, y); + copy_lines_to_ring(y, y2, LST); + redraw(); +} + +void put_lines(lineset_t *u){ + int i, n; + + for (i=0; i< u->num; i++){ + n = u->l[i].n; + ensure_line_length(&(screen.l[i]), u->l[i].lst); + strcpy(screen.l[n].s, u->l[i].s); + screen.l[n].lst = u->l[i].lst; + } +} + + +void undo_change(){ + if (undo_cur >= 0){ + put_lines(& (undo[undo_cur])); + undo_cur --; + } + redraw(); + modified = 1; +} + +void redo_change(){ + if (undo_cur < undo_lst){ + undo_cur ++; + put_lines(& (undo[undo_cur])); + } redraw(); + modified = 1; } + DIR diff --git a/gramscii.h b/gramscii.h @@ -44,6 +44,9 @@ #define VIDEO_NRM 0 #define VIDEO_REV 7 +#define CUR 0x01 +#define LST 0x02 + /** types **/ typedef struct{ @@ -59,6 +62,7 @@ typedef struct{ line_t *l; } lineset_t; + /** MACROS **/ #define MIN(x,y) (x) < (y) ? (x) : (y) @@ -73,6 +77,11 @@ typedef struct{ lineset_t screen; lineset_t cutbuf; +lineset_t *undo; + +int undo_sz; +int undo_cur; +int undo_lst; int WIDTH, HEIGHT; @@ -155,6 +164,8 @@ void ensure_line_length(line_t *l, int len); void ensure_num_lines(lineset_t *ls, int n); void yank_region(int x1, int y1, int x2, int y2); void paste_region(int x1, int y1); - +void copy_lines_to_ring(int y1, int y2, int which); +void undo_change(); +void redo_change(); #endif DIR diff --git a/lineset.c b/lineset.c @@ -58,6 +58,7 @@ void ensure_num_lines(lineset_t *ls, int n){ } } + void dump_lines(lineset_t ls, FILE *f){ int i; for (i=0; i<ls.num ;i++){ @@ -124,3 +125,45 @@ void paste_region(int x1, int y1){ } redraw(); } + +void copy_lines_to_ring(int y1, int y2, int which){ + lineset_t *tmp; + int i, len, *idx; + + if (y1 > y2){ + y1 ^= y2; + y2 ^= y1; + y1 ^= y2; + } + if (which == CUR) + idx = &undo_cur; + else + idx = &undo_lst; + if (*idx == undo_sz - 1){ + undo_sz += 10; + tmp = realloc(undo, undo_sz * sizeof(lineset_t)); + if (tmp == NULL){ + fprintf(stderr, "Error allocating undo buffer"); + exit(1); + } + undo = tmp; + } + (*idx) ++; + 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; + } + undo[*idx].num = y2 - y1 + 1; + if (which == CUR) + 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); + } +#endif +} DIR diff --git a/main.c b/main.c @@ -122,6 +122,12 @@ void commands(FILE *fc){ case 'p': paste(); break; + case 'u': + undo_change(); + break; + case 'U': + redo_change(); + break; case 'q': check_modified(fc);/** FALLTHROUGH **/ case 'Q': DIR diff --git a/screen.c b/screen.c @@ -408,6 +408,10 @@ void init_screen(){ cutbuf.sz = 0; cutbuf.l = NULL; cutbuf.num = 0; + + undo_sz = 0; + undo_cur = -1; + undo_lst = -1; } void find_nonblank_rect(int *x1, int *y1, int *x2, int *y2){