#define _POSIX_C_SOURCE 200112L #include #include #include "gramscii.h" #include "config.h" /** Extern declarations **/ extern int WIDTH, HEIGHT; extern lineset_t screen; /* what is visualised */ extern lineset_t cutbuf; /* cut/paste buffer */ extern lineset_t *undo; /* undo list */ extern int undo_cur;/* undo position */ extern int undo_lst;/* last valid undo position */ extern int mode;/* mode */ extern int dir;/* line direction */ extern int step;/* current step */ extern int x; extern int y; extern char corner; extern char modified; /* set to 1 if screen modified since last save */ /* line and arrow markers */ extern int cur_hl, cur_vl, cur_corn, cur_start, cur_end; extern char line_h; extern char line_v; extern char mark_st; extern char mark_end; /* number of available markers for each type */ extern int hlines_sz; extern int vlines_sz; extern int corners_sz; extern int stmarks_sz; extern int endmarks_sz; extern char autoend; /* set to 1 in auto-arrow mode */ /* Used by draw_arrow to identify the bounding box */ extern int a_miny; extern int a_maxy; /*** drawing-related functions ***/ /*** Lines and markers ***/ void toggle_hline(){ cur_hl = (cur_hl + 1) % hlines_sz; line_h = hlines[cur_hl]; } void toggle_corner(){ cur_corn = (cur_corn + 1 ) % corners_sz; corner = corners[cur_corn]; } void toggle_vline(){ cur_vl = (cur_vl + 1) % vlines_sz; line_v = vlines[cur_vl]; } void toggle_st_mark(){ cur_start = (cur_start + 1 ) % stmarks_sz; mark_st = st_marks[cur_start]; } void toggle_end_mark(){ cur_end = (cur_end+ 1 ) % endmarks_sz; mark_end = end_marks[cur_end]; } int change_style(char c){ switch(c){ case '-': toggle_hline(); break; case '|': toggle_vline(); break; case '+': toggle_corner(); break; case '<': toggle_st_mark(); break; case '>': toggle_end_mark(); break; case '.': reset_styles(); break; default: return 0; } return c; } /***** text, box, arrows *****/ void get_text(FILE *fc){ char c; int orig_x = x; redraw(); copy_lines_to_ring(y, y, PRV_STATE); while((c=fgetc(fc))!=EOF && c != 27){ if(c=='\n'){ set_cur(BG); copy_lines_to_ring(y,y, NEW_STATE); y += 1; copy_lines_to_ring(y, y, PRV_STATE); x = orig_x; } else { set_cur(c); update_current(); modified = 1; x += 1; if (x >= WIDTH) x = orig_x; } check_bound(&x, &y); status_bar(); show_cursor(); } if (modified) copy_lines_to_ring(y, y, NEW_STATE); mode=MOVE; } void draw_box(int x1, int y1, int fix){ int xmin, ymin, xmax, ymax; int i; void (*f)(int, int, char); 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, PRV_STATE); } else f = draw_xy; for(i=xmin+1; i<=xmax; i++){ f(i, ymin, line_h); f(i, ymax, line_h); } for(i=ymin+1; i<=ymax; i++){ f(xmin, i, line_v); f(xmax, i, line_v); } f(xmin, ymin, corner); f(xmin, ymax, corner); f(xmax, ymin, corner); f(xmax, ymax, corner); if (fix == FIX) copy_lines_to_ring(ymin, ymax, NEW_STATE); show_cursor(); } void draw_parallelogram(int x1, int y1, char st, char fix){ int xmin, ymin, xmax, ymax; int dy, minoff, maxoff, xoff, xincr; int i; char lean; void (*f)(int, int, char); xmin = MIN(x, x1); xmax = MAX(x, x1); ymin = MIN(y, y1); ymax = MAX(y, y1); dy = ymax - ymin; if (fix == FIX){ f = set_xy; copy_lines_to_ring(ymin, ymax, PRV_STATE); } else f = draw_xy; if (st == BOX_PARR){ minoff = dy; maxoff = 0; lean = '/'; xincr = -1; } else { minoff = 0; maxoff = dy; lean = '\\'; xincr = +1; } for(i=xmin+1; i<=xmax-dy; i++){ f(i+minoff, ymin, line_h); f(i+maxoff, ymax, line_h); } for(i=ymin+1, xoff=minoff; i<=ymax; i++, xoff += xincr){ f(xmin+(xoff+xincr), i, lean); if (minoff) f(xmax - (minoff - xoff - xincr), i, lean); else f(xmax - (maxoff - xoff - xincr), i, lean); } f(xmin+minoff, ymin, corner); f(xmin+maxoff, ymax, corner); f(xmax-maxoff, ymin, corner); f(xmax-minoff, ymax, corner); if (fix == FIX) copy_lines_to_ring(ymin, ymax, NEW_STATE); show_cursor(); } char flip_par_lean(char st){ if (st == BOX_PARR) return BOX_PARL; else if (st == BOX_PARL) return BOX_PARR; return st; } void draw_trapezium(int x1, int y1, char st, char fix){ int xmin, ymin, xmax, ymax; int dx, dy, ylong, yshort, xoff; int xincr; int i; void (*f)(int, int, char); char left_c, right_c; xmin = MIN(x, x1); xmax = MAX(x, x1); ymin = MIN(y, y1); ymax = MAX(y, y1); dx = (xmax - xmin); dy = ymax - ymin; /* dy = MAX(dx2, dy); */ #ifdef DEBUG fprintf(stderr, "dy: %d dx: %d\n", dy, dx); #endif if (fix == FIX){ f = set_xy; copy_lines_to_ring(ymin, ymax, PRV_STATE); } else f = draw_xy; /* This is valid only for "upper" trapezoids */ if (STYLE_IS(st, BOX_TRAP_U)){ #ifdef DEBUG fprintf(stderr, "This is an 'upward' trapezium\n"); #endif ylong = ymax; yshort = ymin; xoff = dy; xincr = -1; left_c = '/'; right_c = '\\'; } else if (STYLE_IS(st, BOX_TRAP_D)){ #ifdef DEBUG fprintf(stderr, "This is a 'downward' trapezium\n"); #endif ylong = ymin; yshort = ymax; xoff = dy; xincr = +1; right_c = '/'; left_c = '\\'; } /* Long side */ for(i=xmin+1; i<=xmax; i++){ f(i, ylong, line_h); } if (STYLE_IS(st, BOX_TRAP_L)){ /* short side */ left_c = '/'; right_c = line_v; for(i=xmin+xoff;i0) { /* If we are switching between horizontal and vertical, put a "corner" */ if (((cur_dir & DIR_HOR) && (a[i] & DIR_VER)) || ((cur_dir & DIR_VER) && (a[i] & DIR_HOR))){ f(xl, yl, corner); show_cursor(); } } for(j=0; j a_maxy) a_maxy = yl; f(xl, yl, line); } /* f(x,y,mark_end);*/ cur_dir = a[i]; } if (autoend){ if (cur_dir != DIR_N) f(xl,yl, get_mark(cur_dir)); } else f(xl,yl,mark_end); show_cursor(); } void get_arrow(FILE *fc){ char c; int orig_x=x, orig_y=y, arrow_len; static short *arrow = NULL; short *tmp = NULL; static int arrow_sz; if (!arrow){ arrow_sz = 100; arrow = malloc(arrow_sz * sizeof(short)); if (arrow == NULL){ fprintf(stderr, "Unable to allocate arrow"); cleanup(1); } } arrow_len = 0; dir = DIR_N; redraw(); step = 1; draw_arrow(x,y, arrow, 0, NOFIX); while((c=fgetc(fc))!=EOF && c != 27 && c!= 'a' && c != '\n'){ if (change_style(c)) goto update_arrow; if (!move_around(c, fc, 0)) continue; check_bound(&x, &y); /* FIXME: if we are out of bound, do nothing? */ if (arrow_len == arrow_sz){ arrow_sz *=2; tmp = realloc(arrow, arrow_sz * sizeof(short)); if (tmp == NULL){ fprintf(stderr, "Unable to reallocate arrow"); cleanup(1); } arrow = tmp; } if (dir != DIR_N){ arrow[arrow_len++] = dir; arrow[arrow_len++] = step; } redraw(); step = 1; update_arrow: draw_arrow(orig_x, orig_y, arrow, arrow_len, NOFIX); status_bar(); show_cursor(); } if (c == 'a' || c == '\n'){ copy_lines_to_ring(a_miny, a_maxy, PRV_STATE); draw_arrow(orig_x, orig_y, arrow, arrow_len, FIX); copy_lines_to_ring(a_miny, a_maxy, NEW_STATE); modified = 1; } redraw(); mode = MOVE; } void do_erase(int x1, int y1){ int i; switch(dir){ case DIR_R: for(i=x1; i<=x; i++) set_xy(i,y,BG); break; case DIR_L: for(i=x1; i>=x; i--) set_xy(i,y,BG); break; case DIR_U: for(i=y1; i>=y; i--) set_xy(x,i,BG); break; case DIR_D: for(i=y1; i<=y; i++) set_xy(x,i,BG); break; } } void erase(FILE *fc){ /*FIXME: add affected lines to undo */ char c; int orig_x = x, orig_y = y; char first = 1, opened = 0; status_bar(); show_cursor(); while((c=fgetc(fc))!=EOF && c!=27 && c!= 'x' && c != '\n'){ if (!move_around(c, fc, 0)) continue; check_bound(&x, &y); if (first || (y != orig_y && ! opened) || (y == orig_y && x != orig_x && !opened) ){ copy_lines_to_ring(MIN(y, orig_y), MAX(y, orig_y), PRV_STATE); first = 0; opened = 1; } do_erase(orig_x, orig_y); if (y != orig_y && opened){ copy_lines_to_ring(MIN(y, orig_y), MAX(y, orig_y), NEW_STATE); opened = 0; } step = 1; modified = 1; orig_x = x; orig_y = y; redraw(); status_bar(); show_cursor(); } if (opened) copy_lines_to_ring(y, y, NEW_STATE); mode = MOVE; } /*** Visual ***/ void visual_box(FILE *fc){ int orig_x =x, orig_y = y; char c, f = BG; redraw(); step = 1; set_video(VIDEO_REV); draw_box(x,y,NOFIX); while((c=fgetc(fc))!=EOF && c != 27 && c!= 'v' && c != '\n'){ if (!move_around(c, fc, 1)) 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)); copy_lines_to_ring(MIN(orig_y, y), MAX(orig_y, y), PRV_STATE); 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), NEW_STATE); modified = 1; goto vis_exit; break; case 'C':/* crop-to-region */ copy_lines_to_ring(0, HEIGHT-1, PRV_STATE); crop_to_rect(MIN(x, orig_x), MIN(y, orig_y), MAX(x, orig_x), MAX(y, orig_y)); copy_lines_to_ring(0, HEIGHT-1, NEW_STATE); modified = 1; goto vis_exit; break; } check_bound(&x, &y); set_video(VIDEO_NRM); redraw(); step = 1; f = BG; set_video(VIDEO_REV); draw_box(orig_x, orig_y, NOFIX); status_bar(); show_cursor(); } vis_exit: set_video(VIDEO_NRM); redraw(); mode = MOVE; } /*** yank/paste/undo ***/ void paste(){ int y2; y2 = y + cutbuf.num - 1; copy_lines_to_ring(y, y2, PRV_STATE); paste_region(x, y); copy_lines_to_ring(y, y2, NEW_STATE); 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]), strlen(u->l[i].s)); strcpy(screen.l[n].s, u->l[i].s); screen.l[n].lst = strlen(u->l[i].s)-1; } } void undo_change(){ if (undo_cur >= 0){ if (undo_cur > undo_lst) undo_cur = undo_lst; put_lines(& (undo[undo_cur])); undo_cur -= 2; modified = 1; } redraw(); } void redo_change(){ if (undo_cur <= undo_lst-2){ if (undo_cur > 0) put_lines(& (undo[undo_cur+1])); undo_cur +=2; put_lines(& (undo[undo_cur+1])); modified = 1; } redraw(); } /** Comments **/ void get_comment(FILE *fc){ char c; redraw(); while((c = fgetc(fc)) != EOF && c != '\n'); mode = MOVE; }