initial commit - gramscii - A simple editor for ASCII box-and-arrow charts | |
Log | |
Files | |
Refs | |
Tags | |
README | |
LICENSE | |
--- | |
commit 478c03adcdc7d76595a311a0dba682917ab97d62 | |
Author: KatolaZ <[email protected]> | |
Date: Thu, 18 Jul 2019 17:45:38 +0100 | |
initial commit | |
Diffstat: | |
A README | 2 ++ | |
A TODO | 9 +++++++++ | |
A gramscii.c | 353 +++++++++++++++++++++++++++++… | |
3 files changed, 364 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/README b/README | |
@@ -0,0 +1,2 @@ | |
+Simple tool to draw ascii box plots from the terminal. | |
+No need to use 1 million line of javascript code on a browser. | |
diff --git a/TODO b/TODO | |
@@ -0,0 +1,9 @@ | |
++ optimize redraws (i.e., avoid to redraw if possible) | |
+- change cursor shape according to action | |
++ implement box | |
+- implement arrow | |
+- set different box styles | |
++ add status bar | |
+- get screen geometry | |
+- allow scrolling (both vertical and horizontal) | |
+ | |
diff --git a/gramscii.c b/gramscii.c | |
@@ -0,0 +1,353 @@ | |
+/* | |
+* | |
+* This is `gramscii`, a simple tool to create ascii box graphs | |
+* | |
+*/ | |
+ | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <termios.h> | |
+#include <unistd.h> | |
+#include <signal.h> | |
+#include <string.h> | |
+ | |
+#define MOVE 0x00 | |
+#define BOX 0x01 | |
+#define ARROW 0x02 | |
+#define TEXT 0x04 | |
+ | |
+ | |
+#define DIR_R 0x01 | |
+#define DIR_U 0x02 | |
+#define DIR_D 0x04 | |
+#define DIR_L 0x08 | |
+ | |
+ | |
+#define WIDTH 100 | |
+#define HEIGHT 25 | |
+ | |
+#define BG ' ' | |
+#define PTR '+' | |
+#define UND '_' | |
+#define LINE_H '-' | |
+#define LINE_V '|' | |
+#define DBLINE_H '=' | |
+#define DBLINE_V 'u' | |
+#define BLDLINE '#' | |
+#define ARR_L '<' | |
+#define ARR_R '>' | |
+#define ARR_U '^' | |
+#define ARR_D 'v' | |
+ | |
+ | |
+#define MIN(x,y) (x) < (y) ? (x) : (y) | |
+#define MAX(x,y) (x) > (y) ? (x) : (y) | |
+ | |
+char screen[HEIGHT][WIDTH+1]; | |
+ | |
+int state; | |
+int dir; | |
+int x; | |
+int y; | |
+int step; | |
+char cursor; | |
+char corner; | |
+char box_line_h; | |
+char box_line_v; | |
+char arrow_line_h; | |
+char arrow_line_v; | |
+ | |
+struct termios t1, t2; | |
+ | |
+ | |
+void cleanup(int s){ | |
+ | |
+ printf("\033[;H\033[2J"); | |
+ tcsetattr(0, TCSANOW, &t1); | |
+ exit(0); | |
+} | |
+ | |
+void show_cursor(){ | |
+ printf("\033[%d;%df", y+1, x+1); | |
+ //putchar(screen[y][x]); | |
+ //printf("\033[%d;%df", y+1, x+2); | |
+} | |
+ | |
+void set(char c){ | |
+ screen[y][x] = c; | |
+} | |
+ | |
+void set_xy(int x, int y, char c){ | |
+ /* FIXME: check if x and y are valid!!!! */ | |
+ screen[y][x] = c; | |
+} | |
+ | |
+void draw_xy(int x, int y, char c){ | |
+ /* FIXME: check if x and y are valid!!!! */ | |
+ printf("\033[%d;%df",y+1,x+1); | |
+ putchar(c); | |
+} | |
+ | |
+void clear(){ | |
+ screen[y][x] = BG; | |
+} | |
+ | |
+void init_screen(){ | |
+ int i; | |
+ for(i=0; i<HEIGHT; i++){ | |
+ memset(screen[i], ' ', WIDTH); | |
+ screen[i][WIDTH]='\0'; | |
+ } | |
+ cursor = PTR; | |
+ corner = PTR; | |
+ box_line_h = LINE_H; | |
+ box_line_v = LINE_V; | |
+ arrow_line_h = LINE_H; | |
+ arrow_line_v = LINE_V; | |
+} | |
+ | |
+char* state_str(){ | |
+ switch(state){ | |
+ case MOVE: | |
+ return "mv "; | |
+ case TEXT: | |
+ return "txt"; | |
+ case BOX: | |
+ return "box"; | |
+ case ARROW: | |
+ return "arr"; | |
+ } | |
+} | |
+ | |
+ | |
+void status_bar(){ | |
+ | |
+ printf("\033[%d;1f\033[7m", HEIGHT+1); | |
+ printf(" x: %d y: %d -- mode: %s", x, y, state_str()); | |
+ printf("\033[0m"); | |
+} | |
+ | |
+int redraw(){ | |
+ int i; | |
+ | |
+ printf("\033[2J\033[1;1H"); | |
+ for (i=0;i<HEIGHT;i++){ | |
+ fprintf(stdout,"%s\n",screen[i]); | |
+ } | |
+ status_bar(); | |
+ show_cursor(); | |
+ step = 1; | |
+} | |
+ | |
+void update_current(){ | |
+ printf("\033[%d'%df",y+1,x+1); | |
+ putchar(screen[y][x]); | |
+} | |
+ | |
+ | |
+void init(){ | |
+ | |
+ signal(SIGHUP, cleanup); | |
+ signal(SIGINT, cleanup); | |
+ signal(SIGTERM, cleanup); | |
+ signal(SIGQUIT, cleanup); | |
+ | |
+ tcgetattr(0, &t1); | |
+ t2 = t1; | |
+ t2.c_lflag &= ~(ICANON | ECHO); | |
+ tcsetattr(0, TCSANOW, &t2); | |
+ | |
+ init_screen(); | |
+ x = WIDTH/2; | |
+ y = HEIGHT/2; | |
+ redraw(); | |
+} | |
+ | |
+void check_bound(){ | |
+ if (x<0) x=0; | |
+ else if (x>=WIDTH) x = WIDTH-1; | |
+ if (y<0) y=0; | |
+ else if (y>=HEIGHT) y = HEIGHT -1; | |
+} | |
+ | |
+ | |
+void get_text(){ | |
+ char c; | |
+ int orig_x = x, orig_y = y; | |
+ | |
+ //cursor = UND; | |
+ redraw(); | |
+ while((c=getchar())!=EOF && c != 27){ | |
+ if(c=='\n'){ | |
+ set(BG); | |
+ y += 1; | |
+ x = orig_x; | |
+ } | |
+ else { | |
+ set(c); | |
+ update_current(); | |
+ x += 1; | |
+ if (x >= WIDTH) | |
+ x = orig_x; | |
+ } | |
+ check_bound(); | |
+ status_bar(); | |
+ show_cursor(); | |
+ } | |
+ cursor = PTR; | |
+ state=MOVE; | |
+} | |
+ | |
+ | |
+void fix_box(int x1, int y1){ | |
+ | |
+ int xmin, ymin, xmax, ymax; | |
+ int i; | |
+ | |
+ xmin = MIN(x, x1); | |
+ xmax = MAX(x, x1); | |
+ ymin = MIN(y, y1); | |
+ ymax = MAX(y, y1); | |
+ | |
+ | |
+ for(i=xmin+1; i<=xmax; i++){ | |
+ set_xy(i, ymin, box_line_h); | |
+ set_xy(i, ymax, box_line_h); | |
+ } | |
+ for(i=ymin+1; i<=ymax; i++){ | |
+ set_xy(xmin, i, box_line_v); | |
+ set_xy(xmax, i, box_line_v); | |
+ } | |
+ set_xy(xmin, ymin, corner); | |
+ set_xy(xmin, ymax, corner); | |
+ set_xy(xmax, ymin, corner); | |
+ set_xy(xmax, ymax, corner); | |
+} | |
+ | |
+void draw_box(int x1, int y1){ | |
+ | |
+ int xmin, ymin, xmax, ymax; | |
+ int i; | |
+ | |
+ xmin = MIN(x, x1); | |
+ xmax = MAX(x, x1); | |
+ ymin = MIN(y, y1); | |
+ ymax = MAX(y, y1); | |
+ | |
+ | |
+ for(i=xmin+1; i<=xmax; i++){ | |
+ draw_xy(i, ymin, box_line_h); | |
+ draw_xy(i, ymax, box_line_h); | |
+ } | |
+ for(i=ymin+1; i<=ymax; i++){ | |
+ draw_xy(xmin, i, box_line_v); | |
+ draw_xy(xmax, i, box_line_v); | |
+ } | |
+ draw_xy(xmin, ymin, corner); | |
+ draw_xy(xmin, ymax, corner); | |
+ draw_xy(xmax, ymin, corner); | |
+ draw_xy(xmax, ymax, corner); | |
+ show_cursor(); | |
+} | |
+ | |
+ | |
+void get_box(){ | |
+ char c; | |
+ int orig_x=x, orig_y=y; | |
+ | |
+ set(PTR); | |
+ redraw(); | |
+ while((c=getchar())!=EOF && c != 27){ | |
+ switch(c){ | |
+ case 'H': step = 5; | |
+ case 'h': | |
+ x -= step; | |
+ break; | |
+ case 'J': step = 5; | |
+ case 'j': | |
+ y += step; | |
+ break; | |
+ case 'K': step = 5; | |
+ case 'k': | |
+ y -= step; | |
+ break; | |
+ case 'L': step = 5; | |
+ case 'l': | |
+ x += step; | |
+ break; | |
+ } | |
+ check_bound(); | |
+ redraw(); | |
+ draw_box(orig_x, orig_y); | |
+ status_bar(); | |
+ show_cursor(); | |
+ } | |
+ fix_box(orig_x, orig_y); | |
+ redraw(); | |
+} | |
+ | |
+ | |
+int commands(){ | |
+ | |
+ char c; | |
+ while((c=getchar())!=EOF){ | |
+ //screen[y][x]=BG; | |
+ switch(c){ | |
+ case 'H': | |
+ step=5; | |
+ case 'h': | |
+ x-=step; | |
+ break; | |
+ case 'J': | |
+ step=5; | |
+ case 'j': | |
+ y+=step; | |
+ break; | |
+ case 'K': | |
+ step=5; | |
+ case 'k': | |
+ y-=step; | |
+ break; | |
+ case 'L': | |
+ step=5; | |
+ case 'l': | |
+ x+=step; | |
+ break; | |
+ case 'i': | |
+ state = TEXT; | |
+ get_text(); | |
+ break; | |
+ case 'r': | |
+ redraw(); | |
+ break; | |
+ case 'b': | |
+ get_box(); | |
+ break; | |
+ case 'Q': | |
+ case 'q': | |
+ cleanup(0); | |
+ exit(0); | |
+ break; | |
+ default:; | |
+ //statu("got: %d\n", c); | |
+ } | |
+ check_bound(); | |
+ //update(); | |
+ //redraw(); | |
+ status_bar(); | |
+ show_cursor(); | |
+ step = 1; | |
+ } | |
+ | |
+} | |
+ | |
+ | |
+ | |
+ | |
+int main(int argc, char *argv[]){ | |
+ | |
+ init(); | |
+ | |
+ commands(); | |
+ cleanup(0); | |
+} |