draw.c - gramscii - A simple editor for ASCII box-and-arrow charts DIR Log DIR Files DIR Refs DIR Tags DIR README DIR LICENSE --- draw.c (14191B) --- 1 #define _POSIX_C_SOURCE 200112L 2 3 #include <stdlib.h> 4 #include <string.h> 5 6 #include "gramscii.h" 7 #include "config.h" 8 9 /** Extern declarations **/ 10 11 extern int WIDTH, HEIGHT; 12 extern lineset_t screen; /* what is visualised */ 13 extern lineset_t cutbuf; /* cut/paste buffer */ 14 extern lineset_t *undo; /* undo list */ 15 16 extern int undo_cur;/* undo position */ 17 extern int undo_lst;/* last valid undo position */ 18 19 20 extern int mode;/* mode */ 21 extern int dir;/* line direction */ 22 extern int step;/* current step */ 23 extern int x; 24 extern int y; 25 extern char corner; 26 extern char modified; /* set to 1 if screen modified since last save */ 27 28 /* line and arrow markers */ 29 extern int cur_hl, cur_vl, cur_corn, cur_start, cur_end; 30 extern char line_h; 31 extern char line_v; 32 extern char mark_st; 33 extern char mark_end; 34 35 /* number of available markers for each type */ 36 extern int hlines_sz; 37 extern int vlines_sz; 38 extern int corners_sz; 39 extern int stmarks_sz; 40 extern int endmarks_sz; 41 42 43 extern char autoend; /* set to 1 in auto-arrow mode */ 44 45 /* Used by draw_arrow to identify the bounding box */ 46 extern int a_miny; 47 extern int a_maxy; 48 49 /*** drawing-related functions ***/ 50 51 /*** Lines and markers ***/ 52 53 void toggle_hline(){ 54 55 cur_hl = (cur_hl + 1) % hlines_sz; 56 line_h = hlines[cur_hl]; 57 58 } 59 60 void toggle_corner(){ 61 62 cur_corn = (cur_corn + 1 ) % corners_sz; 63 corner = corners[cur_corn]; 64 65 } 66 67 void toggle_vline(){ 68 69 cur_vl = (cur_vl + 1) % vlines_sz; 70 line_v = vlines[cur_vl]; 71 72 } 73 74 void toggle_st_mark(){ 75 76 cur_start = (cur_start + 1 ) % stmarks_sz; 77 mark_st = st_marks[cur_start]; 78 } 79 80 void toggle_end_mark(){ 81 82 cur_end = (cur_end+ 1 ) % endmarks_sz; 83 mark_end = end_marks[cur_end]; 84 } 85 86 int change_style(char c){ 87 switch(c){ 88 case '-': 89 toggle_hline(); 90 break; 91 case '|': 92 toggle_vline(); 93 break; 94 case '+': 95 toggle_corner(); 96 break; 97 case '<': 98 toggle_st_mark(); 99 break; 100 case '>': 101 toggle_end_mark(); 102 break; 103 case '.': 104 reset_styles(); 105 break; 106 default: 107 return 0; 108 } 109 return c; 110 } 111 112 113 114 115 /***** text, box, arrows *****/ 116 117 void get_text(FILE *fc){ 118 char c; 119 int orig_x = x; 120 121 redraw(); 122 copy_lines_to_ring(y, y, PRV_STATE); 123 while((c=fgetc(fc))!=EOF && c != 27){ 124 if(c=='\n'){ 125 set_cur(BG); 126 copy_lines_to_ring(y,y, NEW_STATE); 127 y += 1; 128 copy_lines_to_ring(y, y, PRV_STATE); 129 x = orig_x; 130 } 131 else { 132 set_cur(c); 133 update_current(); 134 modified = 1; 135 x += 1; 136 if (x >= WIDTH) 137 x = orig_x; 138 } 139 check_bound(&x, &y); 140 status_bar(); 141 show_cursor(); 142 } 143 if (modified) 144 copy_lines_to_ring(y, y, NEW_STATE); 145 mode=MOVE; 146 } 147 148 void draw_box(int x1, int y1, int fix){ 149 150 int xmin, ymin, xmax, ymax; 151 int i; 152 void (*f)(int, int, char); 153 154 155 xmin = MIN(x, x1); 156 xmax = MAX(x, x1); 157 ymin = MIN(y, y1); 158 ymax = MAX(y, y1); 159 160 if (fix == FIX){ 161 f = set_xy; 162 copy_lines_to_ring(ymin, ymax, PRV_STATE); 163 } 164 else 165 f = draw_xy; 166 167 for(i=xmin+1; i<=xmax; i++){ 168 f(i, ymin, line_h); 169 f(i, ymax, line_h); 170 } 171 for(i=ymin+1; i<=ymax; i++){ 172 f(xmin, i, line_v); 173 f(xmax, i, line_v); 174 } 175 f(xmin, ymin, corner); 176 f(xmin, ymax, corner); 177 f(xmax, ymin, corner); 178 f(xmax, ymax, corner); 179 if (fix == FIX) 180 copy_lines_to_ring(ymin, ymax, NEW_STATE); 181 show_cursor(); 182 } 183 184 void draw_parallelogram(int x1, int y1, char st, char fix){ 185 int xmin, ymin, xmax, ymax; 186 int dy, minoff, maxoff, xoff, xincr; 187 int i; 188 char lean; 189 void (*f)(int, int, char); 190 191 192 xmin = MIN(x, x1); 193 xmax = MAX(x, x1); 194 ymin = MIN(y, y1); 195 ymax = MAX(y, y1); 196 dy = ymax - ymin; 197 198 if (fix == FIX){ 199 f = set_xy; 200 copy_lines_to_ring(ymin, ymax, PRV_STATE); 201 } 202 else 203 f = draw_xy; 204 if (st == BOX_PARR){ 205 minoff = dy; 206 maxoff = 0; 207 lean = '/'; 208 xincr = -1; 209 } 210 else { 211 minoff = 0; 212 maxoff = dy; 213 lean = '\\'; 214 xincr = +1; 215 } 216 for(i=xmin+1; i<=xmax-dy; i++){ 217 f(i+minoff, ymin, line_h); 218 f(i+maxoff, ymax, line_h); 219 } 220 221 for(i=ymin+1, xoff=minoff; i<=ymax; i++, xoff += xincr){ 222 f(xmin+(xoff+xincr), i, lean); 223 if (minoff) 224 f(xmax - (minoff - xoff - xincr), i, lean); 225 else 226 f(xmax - (maxoff - xoff - xincr), i, lean); 227 } 228 f(xmin+minoff, ymin, corner); 229 f(xmin+maxoff, ymax, corner); 230 f(xmax-maxoff, ymin, corner); 231 f(xmax-minoff, ymax, corner); 232 if (fix == FIX) 233 copy_lines_to_ring(ymin, ymax, NEW_STATE); 234 show_cursor(); 235 236 } 237 238 char flip_par_lean(char st){ 239 if (st == BOX_PARR) 240 return BOX_PARL; 241 else if (st == BOX_PARL) 242 return BOX_PARR; 243 return st; 244 } 245 246 void draw_trapezium(int x1, int y1, char st, char fix){ 247 int xmin, ymin, xmax, ymax; 248 int dx, dy, ylong, yshort, xoff; 249 int xincr; 250 int i; 251 void (*f)(int, int, char); 252 char left_c, right_c; 253 254 xmin = MIN(x, x1); 255 xmax = MAX(x, x1); 256 ymin = MIN(y, y1); 257 ymax = MAX(y, y1); 258 dx = (xmax - xmin); 259 dy = ymax - ymin; 260 /* dy = MAX(dx2, dy); */ 261 #ifdef DEBUG 262 fprintf(stderr, "dy: %d dx: %d\n", dy, dx); 263 #endif 264 if (fix == FIX){ 265 f = set_xy; 266 copy_lines_to_ring(ymin, ymax, PRV_STATE); 267 } 268 else 269 f = draw_xy; 270 271 /* This is valid only for "upper" trapezoids */ 272 if (STYLE_IS(st, BOX_TRAP_U)){ 273 #ifdef DEBUG 274 fprintf(stderr, "This is an 'upward' trapezium\n"); 275 #endif 276 ylong = ymax; 277 yshort = ymin; 278 xoff = dy; 279 xincr = -1; 280 left_c = '/'; 281 right_c = '\\'; 282 } 283 else if (STYLE_IS(st, BOX_TRAP_D)){ 284 #ifdef DEBUG 285 fprintf(stderr, "This is a 'downward' trapezium\n"); 286 #endif 287 ylong = ymin; 288 yshort = ymax; 289 xoff = dy; 290 xincr = +1; 291 right_c = '/'; 292 left_c = '\\'; 293 } 294 /* Long side */ 295 for(i=xmin+1; i<=xmax; i++){ 296 f(i, ylong, line_h); 297 } 298 299 if (STYLE_IS(st, BOX_TRAP_L)){ 300 /* short side */ 301 left_c = '/'; 302 right_c = line_v; 303 for(i=xmin+xoff;i<xmax; i++){ 304 f(i, yshort, line_h); 305 } 306 xoff = dy; 307 if (STYLE_IS(st, BOX_TRAP_D)){ 308 xoff = 0; 309 left_c = '\\'; 310 } 311 for(i=ymin; i<ymax; i++, xoff += xincr){ 312 f(xmin+xoff, i, left_c); 313 f(xmax, i, right_c); 314 } 315 f(xmin+dy, yshort, corner); 316 f(xmax, yshort, corner); 317 } 318 else if (STYLE_IS(st, BOX_TRAP_R)){ 319 right_c = '\\'; 320 left_c = line_v; 321 for(i=xmin; i<xmax-xoff; i++){ 322 f(i, yshort, line_h); 323 } 324 xoff = dy-1; 325 if (STYLE_IS(st, BOX_TRAP_D)){ 326 xoff = 1; 327 right_c = '/'; 328 } 329 for(i=ymin+1; i<ymax; i++, xoff += xincr){ 330 f(xmin, i, left_c); 331 f(xmax-xoff, i, right_c); 332 } 333 f(xmin, yshort, corner); 334 f(xmax-dy, yshort, corner); 335 } 336 else if (STYLE_IS(st, BOX_TRAP_C)){ 337 xoff = dy; 338 for (i=xmin+xoff; i<=xmax-xoff; i++){ 339 f(i, yshort, line_h); 340 } 341 xoff = dy - 1; 342 if (STYLE_IS(st, BOX_TRAP_D)) 343 xoff = 1; 344 for(i=ymin+1; i<ymax; i++, xoff += xincr){ 345 f(xmin + xoff, i, left_c); 346 f(xmax - xoff, i, right_c); 347 } 348 f(xmin+dy, yshort, corner); 349 f(xmax-dy, yshort, corner); 350 } 351 352 353 f(xmin, ylong, corner); 354 f(xmax, ylong, corner); 355 356 357 if (fix == FIX) 358 copy_lines_to_ring(ymin, ymax, NEW_STATE); 359 show_cursor(); 360 361 } 362 363 /* 364 * draw the current box, being it an actual box, a parallelogram, or a 365 * trapezium 366 */ 367 void update_box(int x1, int y1, char st, char fix){ 368 369 if (st == BOX_RECT) 370 draw_box(x1, y1, fix); 371 else if (st & BOX_PAR) 372 draw_parallelogram(x1, y1, st, fix); 373 else if (st & BOX_TRAP) 374 draw_trapezium(x1, y1, st, fix); 375 status_bar(); 376 show_cursor(); 377 } 378 379 char toggle_trap_type(char st){ 380 if (st & BOX_TRAP){ 381 if (st != BOX_TRAP_DR) 382 st += 1; 383 else 384 st = BOX_TRAP_UR; 385 } 386 if (st == BOX_TRAP_D) 387 st += 1; 388 return st; 389 } 390 391 int box_end(char c, char st){ 392 if (c == '\n' || 393 c == 27 || 394 ((st == BOX_RECT) && c == 'b') || 395 ((st & BOX_PAR) && c == 'z') || 396 ((st & BOX_TRAP) && c == 't')) 397 return 1; 398 return 0; 399 } 400 401 /* draw boxes, parallelograms, and trapezia */ 402 void get_box(FILE *fc, char st){ 403 char c; 404 int orig_x=x, orig_y=y; 405 redraw(); 406 step = 1; 407 #ifdef DEBUG 408 fprintf(stderr, "box style: %d\n", st); 409 #endif 410 draw_box(x,y,NOFIX); 411 while((c=fgetc(fc))!=EOF && !box_end(c, st)){ 412 if (c == 'Z' && (st & BOX_PAR)){ 413 st = flip_par_lean(st); 414 redraw(); 415 #ifdef DEBUG 416 fprintf(stderr, "new parallelogram style: %d\n", st); 417 #endif 418 update_box(orig_x, orig_y, st, NOFIX); 419 continue; 420 } 421 else if (c == 'T' && (st & BOX_TRAP)){ 422 st = toggle_trap_type(st); 423 #ifdef DEBUG 424 fprintf(stderr, "new trapezium style: %d\n", st); 425 #endif 426 redraw(); 427 update_box(orig_x, orig_y, st, NOFIX); 428 continue; 429 } 430 if (change_style(c)){ 431 update_box(orig_x, orig_y, st, NOFIX); 432 continue; 433 } 434 if (!move_around(c, fc, 1)) 435 continue; 436 check_bound(&x, &y); 437 redraw(); 438 step = 1; 439 update_box(orig_x, orig_y, st, NOFIX); 440 } 441 if (((st == BOX_RECT ) && (c == 'b' || c == '\n')) || 442 ( (st & BOX_PAR ) && (c == 'z' || c == '\n')) || 443 ( (st & BOX_TRAP ) && (c == 't' || c == '\n'))){ 444 update_box(orig_x, orig_y, st, FIX); 445 modified = 1; 446 } 447 redraw(); 448 mode = MOVE; 449 } 450 451 void draw_arrow(int xl, int yl, short *a, int a_len, int fix){ 452 453 int i, j, cur_dir; 454 char line; 455 void (*f)(int, int, char); 456 457 a_miny = a_maxy = yl; 458 if (fix == FIX) 459 f = set_xy; 460 else 461 f = draw_xy; 462 463 f(xl, yl, mark_st); 464 if (!a_len){ 465 show_cursor(); 466 return; 467 } 468 cur_dir=DIR_N; 469 for (i=0; i<a_len; i+=2){ 470 if (i>0) { 471 /* If we are switching between horizontal and vertical, put a "corner" */ 472 if (((cur_dir & DIR_HOR) && (a[i] & DIR_VER)) || 473 ((cur_dir & DIR_VER) && (a[i] & DIR_HOR))){ 474 f(xl, yl, corner); 475 show_cursor(); 476 } 477 } 478 for(j=0; j<a[i+1]; j++){ 479 line = (a[i] & DIR_L) || (a[i] & DIR_R) ? line_h : line_v; 480 xl += progr_x(a[i]); 481 yl += progr_y(a[i]); 482 check_bound(&xl, &yl); 483 if (yl < a_miny) a_miny = yl; 484 if (yl > a_maxy) a_maxy = yl; 485 f(xl, yl, line); 486 } 487 /* f(x,y,mark_end);*/ 488 cur_dir = a[i]; 489 } 490 if (autoend){ 491 if (cur_dir != DIR_N) 492 f(xl,yl, get_mark(cur_dir)); 493 } 494 else 495 f(xl,yl,mark_end); 496 show_cursor(); 497 } 498 499 void get_arrow(FILE *fc){ 500 501 char c; 502 int orig_x=x, orig_y=y, arrow_len; 503 static short *arrow = NULL; 504 short *tmp = NULL; 505 static int arrow_sz; 506 507 if (!arrow){ 508 arrow_sz = 100; 509 arrow = malloc(arrow_sz * sizeof(short)); 510 if (arrow == NULL){ 511 fprintf(stderr, "Unable to allocate arrow"); 512 cleanup(1); 513 } 514 } 515 arrow_len = 0; 516 dir = DIR_N; 517 518 redraw(); 519 step = 1; 520 draw_arrow(x,y, arrow, 0, NOFIX); 521 while((c=fgetc(fc))!=EOF && c != 27 && c!= 'a' && c != '\n'){ 522 if (change_style(c)) 523 goto update_arrow; 524 if (!move_around(c, fc, 0)) 525 continue; 526 check_bound(&x, &y); 527 /* FIXME: if we are out of bound, do nothing? */ 528 if (arrow_len == arrow_sz){ 529 arrow_sz *=2; 530 tmp = realloc(arrow, arrow_sz * sizeof(short)); 531 if (tmp == NULL){ 532 fprintf(stderr, "Unable to reallocate arrow"); 533 cleanup(1); 534 } 535 arrow = tmp; 536 } 537 if (dir != DIR_N){ 538 arrow[arrow_len++] = dir; 539 arrow[arrow_len++] = step; 540 } 541 redraw(); 542 step = 1; 543 update_arrow: 544 draw_arrow(orig_x, orig_y, arrow, arrow_len, NOFIX); 545 status_bar(); 546 show_cursor(); 547 } 548 if (c == 'a' || c == '\n'){ 549 copy_lines_to_ring(a_miny, a_maxy, PRV_STATE); 550 draw_arrow(orig_x, orig_y, arrow, arrow_len, FIX); 551 copy_lines_to_ring(a_miny, a_maxy, NEW_STATE); 552 modified = 1; 553 } 554 redraw(); 555 mode = MOVE; 556 } 557 558 559 void do_erase(int x1, int y1){ 560 int i; 561 switch(dir){ 562 case DIR_R: 563 for(i=x1; i<=x; i++) set_xy(i,y,BG); 564 break; 565 case DIR_L: 566 for(i=x1; i>=x; i--) set_xy(i,y,BG); 567 break; 568 case DIR_U: 569 for(i=y1; i>=y; i--) set_xy(x,i,BG); 570 break; 571 case DIR_D: 572 for(i=y1; i<=y; i++) set_xy(x,i,BG); 573 break; 574 } 575 } 576 577 578 void erase(FILE *fc){ 579 /*FIXME: add affected lines to undo */ 580 char c; 581 int orig_x = x, orig_y = y; 582 char first = 1, opened = 0; 583 status_bar(); 584 show_cursor(); 585 while((c=fgetc(fc))!=EOF && c!=27 && c!= 'x' && c != '\n'){ 586 if (!move_around(c, fc, 0)) continue; 587 check_bound(&x, &y); 588 if (first || 589 (y != orig_y && ! opened) || 590 (y == orig_y && x != orig_x && !opened) ){ 591 copy_lines_to_ring(MIN(y, orig_y), MAX(y, orig_y), PRV_STATE); 592 first = 0; 593 opened = 1; 594 } 595 do_erase(orig_x, orig_y); 596 if (y != orig_y && opened){ 597 copy_lines_to_ring(MIN(y, orig_y), MAX(y, orig_y), NEW_STATE); 598 opened = 0; 599 } 600 step = 1; 601 modified = 1; 602 orig_x = x; 603 orig_y = y; 604 redraw(); 605 status_bar(); 606 show_cursor(); 607 } 608 if (opened) 609 copy_lines_to_ring(y, y, NEW_STATE); 610 mode = MOVE; 611 } 612 613 614 615 616 617 /*** Visual ***/ 618 619 620 void visual_box(FILE *fc){ 621 int orig_x =x, orig_y = y; 622 char c, f = BG; 623 624 redraw(); 625 step = 1; 626 set_video(VIDEO_REV); 627 draw_box(x,y,NOFIX); 628 while((c=fgetc(fc))!=EOF && c != 27 && c!= 'v' && c != '\n'){ 629 if (!move_around(c, fc, 1)) switch(c){ 630 case 'y': /* yank (copy) */ 631 yank_region(MIN(orig_x,x), MIN(orig_y,y), MAX(orig_x, x), MAX(orig_y, y)); 632 goto vis_exit; 633 break; 634 case 'f':/* fill */ 635 f = get_key(fc, "fill char: "); /** FALLTHROUGH **/ 636 case 'x':/* erase */ 637 if (c == 'x') 638 yank_region(MIN(orig_x,x), MIN(orig_y,y), MAX(orig_x, x), MAX(orig_y, y)); 639 copy_lines_to_ring(MIN(orig_y, y), MAX(orig_y, y), PRV_STATE); 640 erase_box(orig_x, orig_y, f); 641 erase_blank_lines(MIN(y,orig_y), MAX(y, orig_y)); 642 copy_lines_to_ring(MIN(orig_y, y), MAX(orig_y, y), NEW_STATE); 643 644 modified = 1; 645 goto vis_exit; 646 break; 647 case 'C':/* crop-to-region */ 648 copy_lines_to_ring(0, HEIGHT-1, PRV_STATE); 649 crop_to_rect(MIN(x, orig_x), MIN(y, orig_y), MAX(x, orig_x), MAX(y, orig_y)); 650 copy_lines_to_ring(0, HEIGHT-1, NEW_STATE); 651 modified = 1; 652 goto vis_exit; 653 break; 654 } 655 check_bound(&x, &y); 656 set_video(VIDEO_NRM); 657 redraw(); 658 step = 1; 659 f = BG; 660 set_video(VIDEO_REV); 661 draw_box(orig_x, orig_y, NOFIX); 662 status_bar(); 663 show_cursor(); 664 } 665 vis_exit: 666 set_video(VIDEO_NRM); 667 redraw(); 668 mode = MOVE; 669 } 670 671 /*** yank/paste/undo ***/ 672 673 void paste(){ 674 int y2; 675 676 y2 = y + cutbuf.num - 1; 677 copy_lines_to_ring(y, y2, PRV_STATE); 678 paste_region(x, y); 679 copy_lines_to_ring(y, y2, NEW_STATE); 680 redraw(); 681 } 682 683 void put_lines(lineset_t *u){ 684 int i, n; 685 686 for (i=0; i< u->num; i++){ 687 n = u->l[i].n; 688 ensure_line_length(&(screen.l[i]), strlen(u->l[i].s)); 689 strcpy(screen.l[n].s, u->l[i].s); 690 screen.l[n].lst = strlen(u->l[i].s)-1; 691 } 692 } 693 694 695 void undo_change(){ 696 if (undo_cur >= 0){ 697 if (undo_cur > undo_lst) 698 undo_cur = undo_lst; 699 put_lines(& (undo[undo_cur])); 700 undo_cur -= 2; 701 modified = 1; 702 } 703 redraw(); 704 } 705 706 void redo_change(){ 707 if (undo_cur <= undo_lst-2){ 708 if (undo_cur > 0) 709 put_lines(& (undo[undo_cur+1])); 710 undo_cur +=2; 711 put_lines(& (undo[undo_cur+1])); 712 modified = 1; 713 } 714 redraw(); 715 } 716 717 718 /** Comments **/ 719 720 void get_comment(FILE *fc){ 721 char c; 722 redraw(); 723 while((c = fgetc(fc)) != EOF && c != '\n'); 724 mode = MOVE; 725 } 726