diff options
author | KatolaZ <katolaz@freaknet.org> | 2019-07-30 12:15:54 +0100 |
---|---|---|
committer | KatolaZ <katolaz@freaknet.org> | 2019-07-30 12:15:54 +0100 |
commit | eebc645dee0d15871d6cc46f156d424cd916b191 (patch) | |
tree | 82bb51d04d05a3cf1b4937e2cefb70cc282e9993 | |
parent | a99759398841d86928c7ad4d8248f907765cbeb2 (diff) |
yank buffer and initial copy/cut/paste support
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | TODO | 11 | ||||
-rw-r--r-- | draw.c | 11 | ||||
-rw-r--r-- | files.c | 6 | ||||
-rw-r--r-- | gramscii.1 | 20 | ||||
-rw-r--r-- | gramscii.h | 36 | ||||
-rw-r--r-- | lineset.c | 117 | ||||
-rw-r--r-- | main.c | 11 | ||||
-rw-r--r-- | screen.c | 117 |
9 files changed, 227 insertions, 104 deletions
@@ -3,7 +3,7 @@ include config.mk -SRC = main.c draw.c screen.c files.c +SRC = main.c draw.c screen.c files.c lineset.c INC = config.h gramscii.h all: options gramscii @@ -6,8 +6,8 @@ - use [ENTER] to exit from text insert - maybe move "text" mode to "t" - implement ellipse -- filled box (B) -- manage fill character (as for other styles) +- (?) filled box (B) +- (?) manage filled box character (as for other styles) - implement comment (#: ignore until the end of the line) + parse control characters + parse arrows (text-mode will allow movements as well) @@ -15,15 +15,18 @@ - (?) remove extra blanks until EOL when saving to file + visual selection - crop-to - - yank/put + * yank * fill - * delete + * 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) - catch SIGWINCH and react appropriately (after scrolling is enabled) +* put yanked content (p) +* turn screen into a lineset +* change alloc/ensure functions to work on line_t* and lineset_t* * add crop command (C) * reorganise code * change screen management (i.e., dynamic array of lines) @@ -299,9 +299,15 @@ void visual_box(FILE *fc){ draw_box(x,y,NOFIX); while((c=fgetc(fc))!=EOF && c != 27 && c!= 'v' && c != '\n'){ if (!move_around(c, fc)) switch(c){ + case 'y': /* yank (copy) */ + yank_region(MIN(orig_x,x), MIN(orig_y,y), MAX(orig_x, x), MAX(orig_y, y)); + goto vis_exit; + break; case 'f':/* fill */ f = get_key(fc, "fill char: "); /** FALLTHROUGH **/ case 'x':/* erase */ + if (c == 'x') + yank_region(MIN(orig_x,x), MIN(orig_y,y), MAX(orig_x, x), MAX(orig_y, y)); erase_box(orig_x, orig_y, f); erase_blank_lines(MIN(y,orig_y), MAX(y, orig_y)); modified = 1; @@ -323,3 +329,8 @@ vis_exit: redraw(); mode = MOVE; } + +void paste(){ + paste_region(x, y); + redraw(); +} @@ -24,7 +24,7 @@ void write_file(FILE *fc){ return; } for (i=0; i<HEIGHT; i++){ - fprintf(fout, "%s\n", screen[i].s); + fprintf(fout, "%s\n", screen.l[i].s); } fclose(fout); modified = 0; @@ -50,8 +50,8 @@ void load_file(FILE *fc){ get_string(fc, "Load file: ", newfname, 255); if ((fin=fopen(newfname, "r")) != NULL){ i = 0; - while((fgets(screen[i].s, WIDTH+2, fin)) != NULL && i<HEIGHT) - screen[i++].s[WIDTH-1]='\0'; + while((fgets(screen.l[i].s, WIDTH+2, fin)) != NULL && i<HEIGHT) + screen.l[i++].s[WIDTH-1]='\0'; for(;i<HEIGHT; i++){ erase_line(i); } @@ -57,6 +57,12 @@ Crop chart to the largest non-blank region. The first line and the first column of the cropped chart will contain the first non-blank line and the first non-blank column of the original chart, respectively. .TP 5m +.BI p +Paste the content of the yank buffer at the cursor position. The yank +buffer contains the rectangle yanked/cut in +.B visual +mode. +.TP 5m .BI q Quit gramscii, and prompt for a filename if the current screen contains unsaved changes. @@ -340,9 +346,19 @@ commands to highlight a rectangle. Then, you can use one of the following command on the highlighted region: .RS .TP 5m +.BI y +Yank (copy) the highlighted rectangle to the yank buffer. The content of +the yank buffer can be retrieved by using the +.B p +command while in +.B move +mode. The yank buffer is overwritten by subsequent yank/cut commands. +.TP 5m .BI x -Erase region. All the characters in the region are set to the default -background character (space). +Cut region. The content of the highlighted rectangle will be put in the +yank buffer and all the characters in the region are set to the default +background character (space). The yank buffer is overwritten by +subsequent yank/cut commands. .TP 5m .BI f Fill region. gramscii will wait for a character on input and then will @@ -5,13 +5,7 @@ #include <termios.h> #include <unistd.h> -/** types **/ -typedef struct{ - int sz; - int lst; - char *s; -} line_t; /** constants **/ @@ -50,6 +44,20 @@ typedef struct{ #define VIDEO_NRM 0 #define VIDEO_REV 7 +/** types **/ + +typedef struct{ + int sz;/* allocated size*/ + int n;/* line number */ + int lst;/* last visible char (before the first \0) */ + char *s; +} line_t; + +typedef struct{ + int sz;/* allocated size */ + int num;/* number of lines stored */ + line_t *l; +} lineset_t; /** MACROS **/ @@ -63,8 +71,9 @@ typedef struct{ /** global variables **/ -line_t *screen; -int num_lines; +lineset_t screen; +lineset_t cutbuf; + int WIDTH, HEIGHT; int mode; @@ -96,6 +105,7 @@ char visual; char silent; char autoend; + struct termios t1, t2, t3; /** screen-related functions **/ @@ -129,6 +139,7 @@ void get_box(FILE *fc); void get_arrow(FILE *fc); void erase(FILE *fc); void visual_box(FILE *fc); +void paste(); /** file-related functions **/ void write_file(FILE *fc); @@ -136,5 +147,14 @@ void check_modified(FILE *fc); void load_file(FILE *fc); void new_file(FILE *fc); +/** line-related functions **/ + +void dump_lines(lineset_t ls, FILE *f); +void alloc_line(line_t *l); +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); + #endif diff --git a/lineset.c b/lineset.c new file mode 100644 index 0000000..faabb30 --- /dev/null +++ b/lineset.c @@ -0,0 +1,117 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "gramscii.h" + +static int LONG_STEP; + +/* line_t and lineset_t management */ + +void ensure_line_length(line_t *l, int len){ + char *tmp; + + if (l->sz < len + 1){ + tmp = realloc(l->s, (len+1) * 2 * sizeof(char)); + if (!tmp){ + fprintf(stderr, "Unable to allocate string\n"); + exit(1); + } + l->s = tmp; + l->sz = (len + 1) * 2; + } +} + + +void alloc_line(line_t *l){ + char *tmp; + + l->sz = WIDTH+1; + tmp = malloc((l->sz) * sizeof(char)); + if (tmp == NULL){ + fprintf(stderr, "unable to allocate line\n"); + exit(1); + } + l->s = tmp; + memset(l->s, BG, l->sz); + l->lst = -1; + l->s[0]='\0'; +} + +void ensure_num_lines(lineset_t *ls, int n){ + line_t *tmp; + + if (n > ls->sz){ + if (ls->sz == 0) + ls->l=NULL; + tmp = realloc(ls->l, (n + LONG_STEP) * sizeof(line_t)); + if (tmp == NULL){ + fprintf(stderr, "Unable to allocate memory for more lines"); + exit(1); + } + else { + ls->l = tmp; + while ( ls->sz < n + LONG_STEP){ + alloc_line(&(ls->l[ls->sz])); + ls->sz ++; + } + } + } +} + +void dump_lines(lineset_t ls, FILE *f){ + int i; + for (i=0; i<ls.num ;i++){ + fprintf(f, "%d:%s\n", i, ls.l[i].s); + } + fflush(f); +} + +void pad_line_to_length(char *s, int W){ + + int i; + + for (i=strlen(s); i<W; i++){ + s[i] = BG; + } +} + +/* cut/yank/paste/undo management */ + +void yank_region(int x1, int y1, int x2, int y2){ + + int N, W, i; + + N = y2 - y1 + 1; + W = x2 - x1 + 1; + ensure_num_lines(&cutbuf, N); + + for (i=y1; i<=y2; i++){ + ensure_line_length(&(cutbuf.l[i-y1]), W); + memcpy(cutbuf.l[i-y1].s, screen.l[i].s + x1, x2-x1+1); + if (strlen(cutbuf.l[i-y1].s) < W) + pad_line_to_length(cutbuf.l[i-y1].s, W); + cutbuf.l[i-y1].s[W] = '\0'; + cutbuf.l[i-y1].n = i; + } + cutbuf.num = N; +#ifdef DEBUG + dump_lines(cutbuf, stderr); +#endif + +} + + +void paste_region(int x1, int y1){ + int i, curlen; + + i = y1; + while( i < HEIGHT && i < y1 + cutbuf.num){ + memcpy(screen.l[i].s + x1, cutbuf.l[i-y1].s, strlen(cutbuf.l[i-y1].s)); + curlen = strlen(screen.l[i].s); + if (curlen <= x1) + /* double-check this line below */ + pad_line_to_length(screen.l[i].s+curlen, x1 - curlen); + i += 1; + modified = 1; + } +} @@ -30,19 +30,13 @@ char *argv0; -void dump_lines(){ - int i; - for (i=0; i<HEIGHT; i++){ - printf("%s\n", screen[i].s); - } -} void cleanup(int s){ if (!silent) printf("\033[;H\033[2J"); else - dump_lines(); + dump_lines(screen, stdout); tcsetattr(0, TCSANOW, &t1); fflush(stdout); exit(0); @@ -125,6 +119,9 @@ void commands(FILE *fc){ case 'C': crop_to_nonblank(); break; + case 'p': + paste(); + break; case 'q': check_modified(fc);/** FALLTHROUGH **/ case 'Q': @@ -1,9 +1,9 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> #include <termios.h> #include <sys/ioctl.h> +#include <ctype.h> #include "gramscii.h" #include "config.h" @@ -62,7 +62,7 @@ void status_bar(){ else printf(" *%s*", fname ); #ifdef DEBUG - printf(" '%d' ", screen[y].s[x]); + printf(" '%d' ", screen.l[y].s[x]); #endif printf("\033[0m"); fflush(stdout); @@ -109,52 +109,6 @@ int is_yes(char c){ /*** Screen management ***/ -void ensure_line_length(int i, int len){ - char *tmp; - - if (screen[i].sz < len + 1){ - tmp = realloc(screen[i].s, (len+1) * 2 * sizeof(char)); - if (!tmp){ - fprintf(stderr, "Unable to allocate string\n"); - exit(1); - } - screen[i].s = tmp; - screen[i].sz = (len + 1) * 2; - } -} - - -void alloc_line(int i){ - char *tmp; - - screen[i].sz = WIDTH+1; - tmp = malloc((screen[i].sz) * sizeof(char)); - if (tmp == NULL){ - fprintf(stderr, "unable to allocate line %d\n", i+1); - exit(1); - } - screen[i].s = tmp; - memset(screen[i].s, BG, screen[i].sz); - screen[i].lst = -1; - screen[i].s[0]='\0'; -} - -void ensure_num_lines(int n){ - line_t *tmp; - - if (n > num_lines){ - tmp = realloc(screen, (n + LONG_STEP) * sizeof(line_t)); - if (tmp == NULL){ - fprintf(stderr, "Unable to allocate memory for more lines"); - exit(1); - } - else while ( num_lines < n + LONG_STEP){ - alloc_line(num_lines); - num_lines ++; - } - } -} - void show_cursor(){ if (silent) @@ -165,15 +119,15 @@ void show_cursor(){ void set_xy(int _x, int _y, char c){ - ensure_num_lines(_y + 1); - ensure_line_length(_y, _x + 1); - while (screen[_y].lst<_x){ - screen[_y].lst ++; - screen[_y].s[screen[_y].lst] = BG; + ensure_num_lines(&screen, _y + 1); + ensure_line_length(&(screen.l[_y]), _x + 1); + while (screen.l[_y].lst<_x){ + screen.l[_y].lst ++; + screen.l[_y].s[screen.l[_y].lst] = BG; } - screen[_y].s[_x] = c; - if (_x == screen[_y].lst) - screen[_y].s[_x+1] = '\0'; + screen.l[_y].s[_x] = c; + if (_x == screen.l[_y].lst) + screen.l[_y].s[_x+1] = '\0'; } void set_cur(char c){ @@ -193,7 +147,7 @@ void update_current(){ if (silent) return; printf("\033[%d'%df",y+1,x+1); - putchar(screen[y].s[x]); + putchar(screen.l[y].s[x]); fflush(stdout); } @@ -206,20 +160,20 @@ void erase_blank_lines(int y1, int y2){ } for (; y1 <= y2; y1++){ - j = screen[y1].lst; - while (j>=0 && isblank(screen[y1].s[j])) + j = screen.l[y1].lst; + while (j>=0 && isblank(screen.l[y1].s[j])) j--; if (j<0){ - screen[y1].lst = -1; - screen[y1].s[0] = '\0'; + screen.l[y1].lst = -1; + screen.l[y1].s[0] = '\0'; } } } void erase_line(int i){ - screen[i].lst = -1; - screen[i].s[0] = '\0'; + screen.l[i].lst = -1; + screen.l[i].s[0] = '\0'; } void erase_box(int x1, int y1, char c){ @@ -268,7 +222,7 @@ void redraw(){ return; printf("\033[2J\033[1;1H"); for (i=0;i<HEIGHT;i++){ - fprintf(stdout,"%s\n",screen[i].s); + fprintf(stdout,"%s\n",screen.l[i].s); } status_bar(); show_cursor(); @@ -435,14 +389,15 @@ void init_screen(){ WIDTH=80; HEIGHT=24; } - screen = malloc(HEIGHT * sizeof(line_t)); - num_lines = HEIGHT; - if (screen == NULL){ + screen.l = malloc(HEIGHT * sizeof(line_t)); + screen.sz = HEIGHT; + screen.num = HEIGHT; + if (screen.l == NULL){ perror("allocating screen"); exit(1); } for (i=0; i<HEIGHT; i++){ - alloc_line(i); + alloc_line(&(screen.l[i])); } hlines_sz= sizeof(hlines) -1; vlines_sz= sizeof(vlines) -1; @@ -450,6 +405,9 @@ void init_screen(){ stmarks_sz = sizeof(st_marks) - 1; endmarks_sz = sizeof(st_marks) - 1; reset_styles(); + cutbuf.sz = 0; + cutbuf.l = NULL; + cutbuf.num = 0; } void find_nonblank_rect(int *x1, int *y1, int *x2, int *y2){ @@ -457,22 +415,22 @@ void find_nonblank_rect(int *x1, int *y1, int *x2, int *y2){ int i, j; int first; *x1= WIDTH; /** FIXME: replace with num_cols **/ - *y1 = num_lines; + *y1 = screen.num; *x2 = *y2 = 0; - for (i=0; i<num_lines; i++){ - if (screen[i].lst < 0) + for (i=0; i<screen.num; i++){ + if (screen.l[i].lst < 0) continue; *y2 = i; if (i < *y1) *y1 = i; j = 0; - while((j <= screen[i].lst) && isblank(first=screen[i].s[j])) + while((j <= screen.l[i].lst) && isblank(first=screen.l[i].s[j])) j++; if (j < *x1) *x1 = j; - j = screen[i].lst; - while(isblank(screen[i].s[j])) + j = screen.l[i].lst; + while(isblank(screen.l[i].s[j])) j--; if (j > *x2) *x2 = j; @@ -483,13 +441,13 @@ void crop_to_rect(int x1, int y1, int x2, int y2){ int i; for (i=0; i<= y2-y1; i ++){ - ensure_line_length(i, screen[i+y1].lst); - sprintf(screen[i].s, "%s", screen[i+y1].s + x1); - screen[i].lst = screen[i+y1].lst - x1; + ensure_line_length(&(screen.l[i]), screen.l[i+y1].lst); + sprintf(screen.l[i].s, "%s", screen.l[i+y1].s + x1); + screen.l[i].lst = screen.l[i+y1].lst - x1; } while (i<=y2){ - screen[i].lst = -1; - screen[i].s[0]= '\0'; + screen.l[i].lst = -1; + screen.l[i].s[0]= '\0'; i ++; } } @@ -505,3 +463,4 @@ void crop_to_nonblank(){ redraw(); } + |