URI: 
       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){