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