summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKatolaZ <katolaz@freaknet.org>2019-07-30 12:15:54 +0100
committerKatolaZ <katolaz@freaknet.org>2019-07-30 12:15:54 +0100
commiteebc645dee0d15871d6cc46f156d424cd916b191 (patch)
tree82bb51d04d05a3cf1b4937e2cefb70cc282e9993
parenta99759398841d86928c7ad4d8248f907765cbeb2 (diff)
yank buffer and initial copy/cut/paste support
-rw-r--r--Makefile2
-rw-r--r--TODO11
-rw-r--r--draw.c11
-rw-r--r--files.c6
-rw-r--r--gramscii.120
-rw-r--r--gramscii.h36
-rw-r--r--lineset.c117
-rw-r--r--main.c11
-rw-r--r--screen.c117
9 files changed, 227 insertions, 104 deletions
diff --git a/Makefile b/Makefile
index b88dbf5..dd32b20 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/TODO b/TODO
index c04d8f3..71689fc 100644
--- a/TODO
+++ b/TODO
@@ -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)
diff --git a/draw.c b/draw.c
index 9724a1f..3c98534 100644
--- a/draw.c
+++ b/draw.c
@@ -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();
+}
diff --git a/files.c b/files.c
index c007eac..245b29b 100644
--- a/files.c
+++ b/files.c
@@ -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);
}
diff --git a/gramscii.1 b/gramscii.1
index 10a1943..95b2719 100644
--- a/gramscii.1
+++ b/gramscii.1
@@ -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
diff --git a/gramscii.h b/gramscii.h
index b329d89..a08d211 100644
--- a/gramscii.h
+++ b/gramscii.h
@@ -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;
+ }
+}
diff --git a/main.c b/main.c
index 195d8af..d2d5a55 100644
--- a/main.c
+++ b/main.c
@@ -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':
diff --git a/screen.c b/screen.c
index 965d440..3cbfe12 100644
--- a/screen.c
+++ b/screen.c
@@ -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();
}
+