screen.c - gramscii - A simple editor for ASCII box-and-arrow charts | |
Log | |
Files | |
Refs | |
Tags | |
README | |
LICENSE | |
--- | |
screen.c (10512B) | |
--- | |
1 #define _POSIX_C_SOURCE 200112L | |
2 | |
3 #include <stdio.h> | |
4 #include <stdlib.h> | |
5 #include <string.h> | |
6 #include <termios.h> | |
7 #include <sys/ioctl.h> | |
8 #include <ctype.h> | |
9 | |
10 #include "gramscii.h" | |
11 #include "config.h" | |
12 | |
13 /** extern declarations **/ | |
14 | |
15 extern lineset_t screen; /* what is visualised */ | |
16 extern lineset_t cutbuf; /* cut/paste buffer */ | |
17 extern lineset_t *undo; /* undo list */ | |
18 | |
19 extern pos_t marks[26]; /* position marks */ | |
20 extern char mark_map[26]; /* marks map */ | |
21 | |
22 extern int undo_sz;/* allocated size of undo list*/ | |
23 extern int undo_cur;/* undo position */ | |
24 extern int undo_lst;/* last valid undo position */ | |
25 | |
26 extern int WIDTH, HEIGHT; | |
27 | |
28 extern int mode;/* mode */ | |
29 extern int dir;/* line direction */ | |
30 extern int x; | |
31 extern int y; | |
32 extern int step;/* current step */ | |
33 extern int mult;/* current multiplier */ | |
34 | |
35 extern char corner; | |
36 | |
37 /* number of available markers for each type */ | |
38 extern int hlines_sz; | |
39 extern int vlines_sz; | |
40 extern int corners_sz; | |
41 extern int stmarks_sz; | |
42 extern int endmarks_sz; | |
43 /**/ | |
44 | |
45 /* line and arrow markers */ | |
46 extern int cur_hl, cur_vl, cur_corn, cur_start, cur_end; | |
47 extern char line_h; | |
48 extern char line_v; | |
49 extern char mark_st; | |
50 extern char mark_end; | |
51 /**/ | |
52 | |
53 extern char modified; /* set to 1 if screen modified since last save */ | |
54 extern char fname[256]; | |
55 | |
56 | |
57 extern char script; /* set to 1 in script-mode */ | |
58 | |
59 extern struct termios t2, t3; | |
60 | |
61 | |
62 /*** screen management functions ***/ | |
63 | |
64 /*** _isblank ***/ | |
65 | |
66 int _isblank(int c){ | |
67 return c==32 || c==9 ? 1 : 0; | |
68 } | |
69 | |
70 | |
71 /*** Status bar ***/ | |
72 | |
73 char* mode_str(){ | |
74 switch(mode){ | |
75 case MOVE: | |
76 return "mov"; | |
77 case TEXT: | |
78 return "txt"; | |
79 case BOX: | |
80 return "box"; | |
81 case ARROW: | |
82 return "arr"; | |
83 case DEL: | |
84 return "del"; | |
85 case VIS: | |
86 return "vis"; | |
87 case PAR: | |
88 return "par"; | |
89 case REM: | |
90 return "rem"; | |
91 case TRP: | |
92 return "trp"; | |
93 default: | |
94 return "ERR"; | |
95 } | |
96 return "ERR"; | |
97 } | |
98 | |
99 char get_mark(char dir){ | |
100 switch(dir){ | |
101 case DIR_U: | |
102 return '^'; | |
103 case DIR_D: | |
104 return 'v'; | |
105 case DIR_L: | |
106 return '<'; | |
107 case DIR_R: | |
108 return '>'; | |
109 } | |
110 return '>'; | |
111 } | |
112 | |
113 | |
114 void status_bar(){ | |
115 | |
116 if (script) | |
117 return; | |
118 printf("\033[%d;1f\033[7m", HEIGHT+1); | |
119 printf("%*s", WIDTH-1, ""); | |
120 printf("\033[%d;1f\033[7m", HEIGHT+1); | |
121 printf(" x:%3d y:%3d -- MODE:%4s HL:%c VL:%c CN:%c SP:%c EP:%c %… | |
122 x, y, mode_str(), line_h, line_v, corner, mark_st, mark_… | |
123 if (!modified) | |
124 printf(" [%s]", fname ); | |
125 else | |
126 printf(" *%s*", fname ); | |
127 #ifdef DEBUG | |
128 printf(" '%d' ", screen.l[y].s[x]); | |
129 #endif | |
130 printf("\033[0m"); | |
131 fflush(stdout); | |
132 } | |
133 | |
134 char get_key(FILE *fc, char *msg){ | |
135 | |
136 if (script) | |
137 return 0; | |
138 printf("\033[%d;1f\033[7m", HEIGHT+1); | |
139 printf("%*s", WIDTH, ""); | |
140 printf("\033[%d;1f\033[7m", HEIGHT+1); | |
141 printf("%s", msg); | |
142 fflush(stdout); | |
143 printf("\033[0m"); | |
144 fflush(stdout); | |
145 return fgetc(fc); | |
146 } | |
147 | |
148 void get_string(FILE *fc, char *msg, char *s, int sz){ | |
149 | |
150 if (!script){ | |
151 printf("\033[%d;1f\033[7m", HEIGHT+1); | |
152 printf("%*s", WIDTH, ""); | |
153 printf("\033[%d;1f\033[7m", HEIGHT+1); | |
154 | |
155 /* We must activate echo now */ | |
156 t3 = t2; | |
157 t3.c_lflag |= (ECHO | ICANON); | |
158 tcsetattr(0, TCSANOW, &t3); | |
159 printf("%s", msg); | |
160 printf("\033[0m"); | |
161 } | |
162 fgets(s, sz, fc); | |
163 s[strlen(s)-1] = '\0'; | |
164 if (!script){ | |
165 tcsetattr(0, TCSANOW, &t2); | |
166 fflush(stdout); | |
167 } | |
168 } | |
169 | |
170 int is_yes(char c){ | |
171 return c=='y' ? 1 : c == 'Y'? 1 : 0; | |
172 } | |
173 | |
174 /*** Screen management ***/ | |
175 | |
176 | |
177 void show_cursor(){ | |
178 if (script) | |
179 return; | |
180 printf("\033[%d;%df", y+1, x+1); | |
181 fflush(stdout); | |
182 } | |
183 | |
184 | |
185 void set_xy(int _x, int _y, char c){ | |
186 ensure_num_lines(&screen, _y + 1); | |
187 ensure_line_length(&(screen.l[_y]), _x + 1); | |
188 while (screen.l[_y].lst<_x){ | |
189 screen.l[_y].lst ++; | |
190 screen.l[_y].s[screen.l[_y].lst] = BG; | |
191 } | |
192 screen.l[_y].s[_x] = c; | |
193 if (_x == screen.l[_y].lst) | |
194 screen.l[_y].s[_x+1] = '\0'; | |
195 } | |
196 | |
197 void set_cur(char c){ | |
198 set_xy(x, y, c); | |
199 } | |
200 | |
201 void draw_xy(int x, int y, char c){ | |
202 /* FIXME: check if x and y are valid!!!! */ | |
203 if (script) | |
204 return; | |
205 printf("\033[%d;%df",y+1,x+1); | |
206 putchar(c); | |
207 fflush(stdout); | |
208 } | |
209 | |
210 void update_current(){ | |
211 if (script) | |
212 return; | |
213 printf("\033[%d;%df",y+1,x+1); | |
214 putchar(screen.l[y].s[x]); | |
215 fflush(stdout); | |
216 } | |
217 | |
218 void erase_blank_lines(int y1, int y2){ | |
219 int j; | |
220 if (y1 > y2){ | |
221 y1 ^= y2; | |
222 y2 ^= y1; | |
223 y1 ^= y2; | |
224 } | |
225 | |
226 for (; y1 <= y2; y1++){ | |
227 j = screen.l[y1].lst; | |
228 while (j>=0 && _isblank(screen.l[y1].s[j])) | |
229 j--; | |
230 if (j<0){ | |
231 screen.l[y1].lst = -1; | |
232 screen.l[y1].s[0] = '\0'; | |
233 } | |
234 } | |
235 } | |
236 | |
237 | |
238 void erase_line(int i){ | |
239 screen.l[i].lst = -1; | |
240 screen.l[i].s[0] = '\0'; | |
241 } | |
242 | |
243 void erase_box(int x1, int y1, char c){ | |
244 int x_incr, y_incr, i; | |
245 | |
246 x_incr = x1 < x? +1: -1; | |
247 y_incr = y1 < y? +1: -1; | |
248 do{ | |
249 i = y1; | |
250 do{ | |
251 set_xy(x1, i, c); | |
252 } while(i != y && (1 | (i += y_incr))); | |
253 } while(x1 != x && (1 | (x1 += x_incr))); | |
254 | |
255 } | |
256 | |
257 void erase_screen(){ | |
258 int i; | |
259 for(i=0;i<HEIGHT; i++) | |
260 erase_line(i); | |
261 } | |
262 | |
263 void check_bound(int *x, int *y){ | |
264 if (*x<0) *x=0; | |
265 else if (*x>=WIDTH) *x = WIDTH-1; | |
266 if (*y<0) *y=0; | |
267 else if (*y>=HEIGHT) *y = HEIGHT -1; | |
268 } | |
269 | |
270 void reset_styles(){ | |
271 | |
272 cur_corn = 0; | |
273 corner = corners[0]; | |
274 cur_hl = cur_vl = 0; | |
275 cur_start = cur_end = 0; | |
276 line_h = hlines[cur_hl]; | |
277 line_v = vlines[cur_vl]; | |
278 mark_st = st_marks[cur_start]; | |
279 mark_end = end_marks[cur_end]; | |
280 } | |
281 | |
282 void redraw(){ | |
283 int i; | |
284 | |
285 if (script) | |
286 return; | |
287 printf("\033[2J\033[1;1H"); | |
288 for (i=0;i<HEIGHT;i++){ | |
289 fprintf(stdout,"%s\n",screen.l[i].s); | |
290 } | |
291 status_bar(); | |
292 show_cursor(); | |
293 } | |
294 | |
295 void go_to(int where){ | |
296 switch(where){ | |
297 case HOME: | |
298 x = y = 0; | |
299 break; | |
300 case END: | |
301 x = WIDTH-1; | |
302 y = HEIGHT-1; | |
303 break; | |
304 case MIDDLE: | |
305 x = WIDTH/2; | |
306 y = HEIGHT/2; | |
307 break; | |
308 } | |
309 check_bound(&x, &y); | |
310 show_cursor(); | |
311 } | |
312 | |
313 void handle_goto(FILE *fc, char global){ | |
314 char c; | |
315 c=fgetc(fc); | |
316 switch(c){ | |
317 case 'h': | |
318 dir = DIR_L; | |
319 step = x; | |
320 x = 0; | |
321 break; | |
322 case 'l': | |
323 dir = DIR_R; | |
324 step = WIDTH - x -1; | |
325 x = WIDTH - 1; | |
326 break; | |
327 case 'j': | |
328 dir = DIR_D; | |
329 step = HEIGHT - y-1; | |
330 y = HEIGHT - 1; | |
331 break; | |
332 case 'k': | |
333 dir = DIR_U; | |
334 step = y; | |
335 y = 0; | |
336 break; | |
337 case 'g': | |
338 if (global){ | |
339 dir = DIR_N; | |
340 go_to(HOME); | |
341 } else step = 0; | |
342 break; | |
343 case 'G': | |
344 if (global){ | |
345 dir = DIR_N; | |
346 go_to(END); | |
347 } else step = 0; | |
348 break; | |
349 case 'm': | |
350 if (global){ | |
351 dir = DIR_N; | |
352 go_to(MIDDLE); | |
353 } else step = 0; | |
354 break; | |
355 case '\'': | |
356 c = tolower(fgetc(fc)); | |
357 if (global) { | |
358 dir = DIR_N; | |
359 if (isalpha(c) && mark_map[c - 'a']){ | |
360 x = marks[c - 'a'].x; | |
361 y = marks[c - 'a'].y; | |
362 #ifdef DEBUG | |
363 fprintf(stderr, "going to valid … | |
364 #endif | |
365 } | |
366 #ifdef DEBUG | |
367 else | |
368 fprintf(stderr, "invalid mark '%… | |
369 #endif | |
370 } else step = 0; | |
371 break; | |
372 | |
373 } | |
374 | |
375 #ifdef DEBUG | |
376 fprintf(stderr, "global move: dir: %d x: %d y: %d\n", dir, x, y); | |
377 #endif | |
378 check_bound(&x, &y); | |
379 show_cursor(); | |
380 } | |
381 | |
382 | |
383 int get_escape(FILE *fc){ | |
384 char c[4]; | |
385 | |
386 c[0] = fgetc(fc); | |
387 if (c[0] == '['){ | |
388 c[1] = fgetc(fc); | |
389 switch(c[1]){ | |
390 case 'D': | |
391 dir = DIR_L; | |
392 x -= step; | |
393 break; | |
394 case 'B': | |
395 dir = DIR_D; | |
396 y += step; | |
397 break; | |
398 case 'A': | |
399 dir = DIR_U; | |
400 y -= step; | |
401 break; | |
402 case 'C': | |
403 dir = DIR_R; | |
404 x += step; | |
405 break; | |
406 } | |
407 return 1; | |
408 } | |
409 else{ | |
410 ungetc(c[0], fc); | |
411 return 0; | |
412 } | |
413 | |
414 } | |
415 | |
416 | |
417 int move_around(char c, FILE *fc, char global){ | |
418 | |
419 if (isdigit(c)){ | |
420 if (mult) | |
421 mult *=10; | |
422 mult += c - '0'; | |
423 return 0; | |
424 } | |
425 #ifdef DEBUG | |
426 fprintf(stderr, "got char: %c\n", c); | |
427 #endif | |
428 switch(c){ | |
429 case 27: /* control sequence? */ | |
430 c = get_escape(fc); | |
431 break; | |
432 case 'H': step = LONG_STEP;/** FALLTHROUGH **/ | |
433 case 'h': | |
434 dir = DIR_L; | |
435 if (mult) | |
436 step *= mult; | |
437 x -= step; | |
438 break; | |
439 case 'J': step = LONG_STEP;/** FALLTHROUGH **/ | |
440 case 'j': | |
441 if (mult) | |
442 step *= mult; | |
443 dir = DIR_D; | |
444 y += step; | |
445 break; | |
446 case 'K': step = LONG_STEP;/** FALLTHROUGH **/ | |
447 case 'k': | |
448 if (mult) | |
449 step *= mult; | |
450 dir = DIR_U; | |
451 y -= step; | |
452 break; | |
453 case 'L': step = LONG_STEP;/** FALLTHROUGH **/ | |
454 case 'l': | |
455 if (mult) | |
456 step *= mult; | |
457 dir = DIR_R; | |
458 x += step; | |
459 break; | |
460 case 'g': | |
461 #ifdef DEBUG | |
462 fprintf(stderr, "before handle_goto: step: %d x:… | |
463 #endif | |
464 handle_goto(fc, global); | |
465 #ifdef DEBUG | |
466 fprintf(stderr, "after handle_goto: step: %d x: … | |
467 #endif | |
468 break; | |
469 default: | |
470 return 0; | |
471 } | |
472 mult = 0; | |
473 return c; | |
474 } | |
475 | |
476 | |
477 void set_video(int v){ | |
478 if (script) | |
479 return; | |
480 printf("\033[%dm", v); | |
481 fflush(stdout); | |
482 } | |
483 | |
484 | |
485 void init_screen(){ | |
486 int i; | |
487 struct winsize wsz; | |
488 | |
489 if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz)){ | |
490 WIDTH=wsz.ws_col - 2; | |
491 HEIGHT=wsz.ws_row - 1; | |
492 } | |
493 else { | |
494 WIDTH=80; | |
495 HEIGHT=24; | |
496 } | |
497 screen.l = malloc(HEIGHT * sizeof(line_t)); | |
498 screen.sz = HEIGHT; | |
499 screen.num = HEIGHT; | |
500 if (screen.l == NULL){ | |
501 fprintf(stderr, "Error allocating screen"); | |
502 cleanup(1); | |
503 } | |
504 for (i=0; i<HEIGHT; i++){ | |
505 alloc_line(&(screen.l[i])); | |
506 } | |
507 /* init markers */ | |
508 hlines_sz= sizeof(hlines) -1; | |
509 vlines_sz= sizeof(vlines) -1; | |
510 corners_sz = sizeof(corners) -1; | |
511 stmarks_sz = sizeof(st_marks) - 1; | |
512 endmarks_sz = sizeof(st_marks) - 1; | |
513 reset_styles(); | |
514 /* init undo ring */ | |
515 cutbuf.sz = 0; | |
516 cutbuf.l = NULL; | |
517 cutbuf.num = 0; | |
518 undo = NULL; | |
519 undo_sz = 0; | |
520 undo_cur = -2; | |
521 undo_lst = -2; | |
522 /* init pos marks */ | |
523 memset(mark_map, 0, 26 * sizeof(char)); | |
524 } | |
525 | |
526 void find_nonblank_rect(int *x1, int *y1, int *x2, int *y2){ | |
527 | |
528 int i, j; | |
529 int first; | |
530 *x1= WIDTH; /** FIXME: replace with num_cols **/ | |
531 *y1 = screen.num; | |
532 *x2 = *y2 = 0; | |
533 | |
534 for (i=0; i<screen.num; i++){ | |
535 if (screen.l[i].lst < 0) | |
536 continue; | |
537 *y2 = i; | |
538 if (i < *y1) | |
539 *y1 = i; | |
540 j = 0; | |
541 while((j <= screen.l[i].lst) && _isblank(first=screen.l… | |
542 j++; | |
543 if (j < *x1) | |
544 *x1 = j; | |
545 j = screen.l[i].lst; | |
546 while(_isblank(screen.l[i].s[j])) | |
547 j--; | |
548 if (j > *x2) | |
549 *x2 = j; | |
550 } | |
551 } | |
552 | |
553 void crop_to_rect(int x1, int y1, int x2, int y2){ | |
554 int i; | |
555 | |
556 for (i=0; i<= y2-y1; i ++){ | |
557 ensure_line_length(&(screen.l[i]), screen.l[i+y1].lst+1); | |
558 sprintf(screen.l[i].s, "%s", screen.l[i+y1].s + x1); | |
559 screen.l[i].lst = x2 - x1; | |
560 screen.l[i].s[screen.l[i].lst + 1] = '\0'; | |
561 } | |
562 while (i< HEIGHT){ | |
563 screen.l[i].lst = -1; | |
564 screen.l[i].s[0]= '\0'; | |
565 i ++; | |
566 } | |
567 } | |
568 | |
569 void crop_to_nonblank(){ | |
570 int x1, x2, y1, y2; | |
571 find_nonblank_rect(&x1, &y1, &x2, &y2); | |
572 #ifdef DEBUG | |
573 fprintf(stderr, "crop rectangle: (%d, %d)-(%d, %d)\n", x1, y1, x… | |
574 #endif | |
575 copy_lines_to_ring(0, y2, PRV_STATE); | |
576 crop_to_rect(x1, y1, x2, y2); | |
577 copy_lines_to_ring(0, y2, NEW_STATE); | |
578 modified=1; | |
579 redraw(); | |
580 } | |
581 | |
582 /** position marks **/ | |
583 | |
584 void mark_pos(FILE *fc){ | |
585 | |
586 char c; | |
587 c = tolower(fgetc(fc)); | |
588 if (isalpha(c)){ | |
589 marks[c - 'a'].x = x; | |
590 marks[c - 'a'].y = y; | |
591 mark_map[c - 'a'] = 1; | |
592 #ifdef DEBUG | |
593 fprintf(stderr, "marking pos (%d, %d) as '%c'\n", x, y, … | |
594 #endif | |
595 } | |
596 } |