screen.c - gramscii - A simple editor for ASCII box-and-arrow charts
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Tags
(DIR) README
(DIR) 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 %10s",
122 x, y, mode_str(), line_h, line_v, corner, mark_st, mark_end, "");
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 mark '%c' (%d, %d)\n", c, x, y);
364 #endif
365 }
366 #ifdef DEBUG
367 else
368 fprintf(stderr, "invalid mark '%c'\n", c);
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: %d y: %d global: %d\n", step, x, y, global);
463 #endif
464 handle_goto(fc, global);
465 #ifdef DEBUG
466 fprintf(stderr, "after handle_goto: step: %d x: %d y: %d global: %d\n", step, x, y, global);
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[i].s[j]))
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, x2, y2);
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, c);
594 #endif
595 }
596 }