1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include <fcall.h>
10 #include <bio.h>
11 #include <plumb.h>
12 #include "dat.h"
13 #include "fns.h"
14
15 void
rowinit(Row * row,Rectangle r)16 rowinit(Row *row, Rectangle r)
17 {
18 Rectangle r1;
19 Text *t;
20
21 draw(screen, r, display->white, nil, ZP);
22 row->r = r;
23 row->col = nil;
24 row->ncol = 0;
25 r1 = r;
26 r1.max.y = r1.min.y + font->height;
27 t = &row->tag;
28 textinit(t, fileaddtext(nil, t), r1, rfget(FALSE, FALSE, FALSE, nil), tagcols);
29 t->what = Rowtag;
30 t->row = row;
31 t->w = nil;
32 t->col = nil;
33 r1.min.y = r1.max.y;
34 r1.max.y += Border;
35 draw(screen, r1, display->black, nil, ZP);
36 textinsert(t, 0, L"Newcol Kill Putall Dump Exit ", 29, TRUE);
37 textsetselect(t, t->file->nc, t->file->nc);
38 }
39
40 Column*
rowadd(Row * row,Column * c,int x)41 rowadd(Row *row, Column *c, int x)
42 {
43 Rectangle r, r1;
44 Column *d;
45 int i;
46
47 d = nil;
48 r = row->r;
49 r.min.y = row->tag.r.max.y+Border;
50 if(x<r.min.x && row->ncol>0){ /*steal 40% of last column by default */
51 d = row->col[row->ncol-1];
52 x = d->r.min.x + 3*Dx(d->r)/5;
53 }
54 /* look for column we'll land on */
55 for(i=0; i<row->ncol; i++){
56 d = row->col[i];
57 if(x < d->r.max.x)
58 break;
59 }
60 if(row->ncol > 0){
61 if(i < row->ncol)
62 i++; /* new column will go after d */
63 r = d->r;
64 if(Dx(r) < 100)
65 return nil;
66 draw(screen, r, display->white, nil, ZP);
67 r1 = r;
68 r1.max.x = min(x, r.max.x-50);
69 if(Dx(r1) < 50)
70 r1.max.x = r1.min.x+50;
71 colresize(d, r1);
72 r1.min.x = r1.max.x;
73 r1.max.x = r1.min.x+Border;
74 draw(screen, r1, display->black, nil, ZP);
75 r.min.x = r1.max.x;
76 }
77 if(c == nil){
78 c = emalloc(sizeof(Column));
79 colinit(c, r);
80 incref(&reffont);
81 }else
82 colresize(c, r);
83 c->row = row;
84 c->tag.row = row;
85 row->col = realloc(row->col, (row->ncol+1)*sizeof(Column*));
86 memmove(row->col+i+1, row->col+i, (row->ncol-i)*sizeof(Column*));
87 row->col[i] = c;
88 row->ncol++;
89 clearmouse();
90 return c;
91 }
92
93 void
rowresize(Row * row,Rectangle r)94 rowresize(Row *row, Rectangle r)
95 {
96 int i, dx, odx;
97 Rectangle r1, r2;
98 Column *c;
99
100 dx = Dx(r);
101 odx = Dx(row->r);
102 row->r = r;
103 r1 = r;
104 r1.max.y = r1.min.y + font->height;
105 textresize(&row->tag, r1, TRUE);
106 r1.min.y = r1.max.y;
107 r1.max.y += Border;
108 draw(screen, r1, display->black, nil, ZP);
109 r.min.y = r1.max.y;
110 r1 = r;
111 r1.max.x = r1.min.x;
112 for(i=0; i<row->ncol; i++){
113 c = row->col[i];
114 r1.min.x = r1.max.x;
115 if(i == row->ncol-1)
116 r1.max.x = r.max.x;
117 else
118 r1.max.x = r1.min.x+Dx(c->r)*dx/odx;
119 if(i > 0){
120 r2 = r1;
121 r2.max.x = r2.min.x+Border;
122 draw(screen, r2, display->black, nil, ZP);
123 r1.min.x = r2.max.x;
124 }
125 colresize(c, r1);
126 }
127 }
128
129 void
rowdragcol(Row * row,Column * c,int)130 rowdragcol(Row *row, Column *c, int)
131 {
132 Rectangle r;
133 int i, b, x;
134 Point p, op;
135 Column *d;
136
137 clearmouse();
138 setcursor(mousectl, &boxcursor);
139 b = mouse->buttons;
140 op = mouse->xy;
141 while(mouse->buttons == b)
142 readmouse(mousectl);
143 setcursor(mousectl, nil);
144 if(mouse->buttons){
145 while(mouse->buttons)
146 readmouse(mousectl);
147 return;
148 }
149
150 for(i=0; i<row->ncol; i++)
151 if(row->col[i] == c)
152 goto Found;
153 error("can't find column");
154
155 Found:
156 p = mouse->xy;
157 if((abs(p.x-op.x)<5 && abs(p.y-op.y)<5))
158 return;
159 if((i>0 && p.x<row->col[i-1]->r.min.x) || (i<row->ncol-1 && p.x>c->r.max.x)){
160 /* shuffle */
161 x = c->r.min.x;
162 rowclose(row, c, FALSE);
163 if(rowadd(row, c, p.x) == nil) /* whoops! */
164 if(rowadd(row, c, x) == nil) /* WHOOPS! */
165 if(rowadd(row, c, -1)==nil){ /* shit! */
166 rowclose(row, c, TRUE);
167 return;
168 }
169 colmousebut(c);
170 return;
171 }
172 if(i == 0)
173 return;
174 d = row->col[i-1];
175 if(p.x < d->r.min.x+80+Scrollwid)
176 p.x = d->r.min.x+80+Scrollwid;
177 if(p.x > c->r.max.x-80-Scrollwid)
178 p.x = c->r.max.x-80-Scrollwid;
179 r = d->r;
180 r.max.x = c->r.max.x;
181 draw(screen, r, display->white, nil, ZP);
182 r.max.x = p.x;
183 colresize(d, r);
184 r = c->r;
185 r.min.x = p.x;
186 r.max.x = r.min.x;
187 r.max.x += Border;
188 draw(screen, r, display->black, nil, ZP);
189 r.min.x = r.max.x;
190 r.max.x = c->r.max.x;
191 colresize(c, r);
192 colmousebut(c);
193 }
194
195 void
rowclose(Row * row,Column * c,int dofree)196 rowclose(Row *row, Column *c, int dofree)
197 {
198 Rectangle r;
199 int i;
200
201 for(i=0; i<row->ncol; i++)
202 if(row->col[i] == c)
203 goto Found;
204 error("can't find column");
205 Found:
206 r = c->r;
207 if(dofree)
208 colcloseall(c);
209 memmove(row->col+i, row->col+i+1, (row->ncol-i)*sizeof(Column*));
210 row->ncol--;
211 row->col = realloc(row->col, row->ncol*sizeof(Column*));
212 if(row->ncol == 0){
213 draw(screen, r, display->white, nil, ZP);
214 return;
215 }
216 if(i == row->ncol){ /* extend last column right */
217 c = row->col[i-1];
218 r.min.x = c->r.min.x;
219 r.max.x = row->r.max.x;
220 }else{ /* extend next window left */
221 c = row->col[i];
222 r.max.x = c->r.max.x;
223 }
224 draw(screen, r, display->white, nil, ZP);
225 colresize(c, r);
226 }
227
228 Column*
rowwhichcol(Row * row,Point p)229 rowwhichcol(Row *row, Point p)
230 {
231 int i;
232 Column *c;
233
234 for(i=0; i<row->ncol; i++){
235 c = row->col[i];
236 if(ptinrect(p, c->r))
237 return c;
238 }
239 return nil;
240 }
241
242 Text*
rowwhich(Row * row,Point p)243 rowwhich(Row *row, Point p)
244 {
245 Column *c;
246
247 if(ptinrect(p, row->tag.all))
248 return &row->tag;
249 c = rowwhichcol(row, p);
250 if(c)
251 return colwhich(c, p);
252 return nil;
253 }
254
255 Text*
rowtype(Row * row,Rune r,Point p)256 rowtype(Row *row, Rune r, Point p)
257 {
258 Window *w;
259 Text *t;
260
261 clearmouse();
262 qlock(row);
263 if(bartflag)
264 t = barttext;
265 else
266 t = rowwhich(row, p);
267 if(t!=nil && !(t->what==Tag && ptinrect(p, t->scrollr))){
268 w = t->w;
269 if(w == nil)
270 texttype(t, r);
271 else{
272 winlock(w, 'K');
273 wintype(w, t, r);
274 /* Expand tag if necessary */
275 if(t->what == Tag){
276 t->w->tagsafe = FALSE;
277 if(r == '\n')
278 t->w->tagexpand = TRUE;
279 winresize(w, w->r, TRUE, TRUE);
280 }
281 winunlock(w);
282 }
283 }
284 qunlock(row);
285 return t;
286 }
287
288 int
rowclean(Row * row)289 rowclean(Row *row)
290 {
291 int clean;
292 int i;
293
294 clean = TRUE;
295 for(i=0; i<row->ncol; i++)
296 clean &= colclean(row->col[i]);
297 return clean;
298 }
299
300 void
rowdump(Row * row,char * file)301 rowdump(Row *row, char *file)
302 {
303 int i, j, fd, m, n, dumped;
304 uint q0, q1;
305 Biobuf *b;
306 char *buf, *a, *fontname;
307 Rune *r;
308 Column *c;
309 Window *w, *w1;
310 Text *t;
311
312 if(row->ncol == 0)
313 return;
314 buf = fbufalloc();
315 if(file == nil){
316 if(home == nil){
317 warning(nil, "can't find file for dump: $home not defined\n");
318 goto Rescue;
319 }
320 sprint(buf, "%s/acme.dump", home);
321 file = buf;
322 }
323 fd = create(file, OWRITE, 0600);
324 if(fd < 0){
325 warning(nil, "can't open %s: %r\n", file);
326 goto Rescue;
327 }
328 b = emalloc(sizeof(Biobuf));
329 Binit(b, fd, OWRITE);
330 r = fbufalloc();
331 Bprint(b, "%s\n", wdir);
332 Bprint(b, "%s\n", fontnames[0]);
333 Bprint(b, "%s\n", fontnames[1]);
334 for(i=0; i<row->ncol; i++){
335 c = row->col[i];
336 Bprint(b, "%11d", 100*(c->r.min.x-row->r.min.x)/Dx(row->r));
337 if(i == row->ncol-1)
338 Bputc(b, '\n');
339 else
340 Bputc(b, ' ');
341 }
342 for(i=0; i<row->ncol; i++){
343 c = row->col[i];
344 for(j=0; j<c->nw; j++)
345 c->w[j]->body.file->dumpid = 0;
346 }
347 for(i=0; i<row->ncol; i++){
348 c = row->col[i];
349 for(j=0; j<c->nw; j++){
350 w = c->w[j];
351 wincommit(w, &w->tag);
352 t = &w->body;
353 /* windows owned by others get special treatment */
354 if(w->nopen[QWevent] > 0)
355 if(w->dumpstr == nil)
356 continue;
357 /* zeroxes of external windows are tossed */
358 if(t->file->ntext > 1)
359 for(n=0; n<t->file->ntext; n++){
360 w1 = t->file->text[n]->w;
361 if(w == w1)
362 continue;
363 if(w1->nopen[QWevent])
364 goto Continue2;
365 }
366 fontname = "";
367 if(t->reffont->f != font)
368 fontname = t->reffont->f->name;
369 if(t->file->nname)
370 a = runetobyte(t->file->name, t->file->nname);
371 else
372 a = emalloc(1);
373 if(t->file->dumpid){
374 dumped = FALSE;
375 Bprint(b, "x%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid,
376 w->body.q0, w->body.q1,
377 100*(w->r.min.y-c->r.min.y)/Dy(c->r),
378 fontname);
379 }else if(w->dumpstr){
380 dumped = FALSE;
381 Bprint(b, "e%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid,
382 0, 0,
383 100*(w->r.min.y-c->r.min.y)/Dy(c->r),
384 fontname);
385 }else if((w->dirty==FALSE && access(a, 0)==0) || w->isdir){
386 dumped = FALSE;
387 t->file->dumpid = w->id;
388 Bprint(b, "f%11d %11d %11d %11d %11d %s\n", i, w->id,
389 w->body.q0, w->body.q1,
390 100*(w->r.min.y-c->r.min.y)/Dy(c->r),
391 fontname);
392 }else{
393 dumped = TRUE;
394 t->file->dumpid = w->id;
395 Bprint(b, "F%11d %11d %11d %11d %11d %11d %s\n", i, j,
396 w->body.q0, w->body.q1,
397 100*(w->r.min.y-c->r.min.y)/Dy(c->r),
398 w->body.file->nc, fontname);
399 }
400 free(a);
401 winctlprint(w, buf, 0);
402 Bwrite(b, buf, strlen(buf));
403 m = min(RBUFSIZE, w->tag.file->nc);
404 bufread(w->tag.file, 0, r, m);
405 n = 0;
406 while(n<m && r[n]!='\n')
407 n++;
408 r[n++] = '\n';
409 Bprint(b, "%.*S", n, r);
410 if(dumped){
411 q0 = 0;
412 q1 = t->file->nc;
413 while(q0 < q1){
414 n = q1 - q0;
415 if(n > BUFSIZE/UTFmax)
416 n = BUFSIZE/UTFmax;
417 bufread(t->file, q0, r, n);
418 Bprint(b, "%.*S", n, r);
419 q0 += n;
420 }
421 }
422 if(w->dumpstr){
423 if(w->dumpdir)
424 Bprint(b, "%s\n%s\n", w->dumpdir, w->dumpstr);
425 else
426 Bprint(b, "\n%s\n", w->dumpstr);
427 }
428 Continue2:;
429 }
430 }
431 Bterm(b);
432 close(fd);
433 free(b);
434 fbuffree(r);
435
436 Rescue:
437 fbuffree(buf);
438 }
439
440 static
441 char*
rdline(Biobuf * b,int * linep)442 rdline(Biobuf *b, int *linep)
443 {
444 char *l;
445
446 l = Brdline(b, '\n');
447 if(l)
448 (*linep)++;
449 return l;
450 }
451
452 /*
453 * Get font names from load file so we don't load fonts we won't use
454 */
455 void
rowloadfonts(char * file)456 rowloadfonts(char *file)
457 {
458 int i;
459 Biobuf *b;
460 char *l;
461
462 b = Bopen(file, OREAD);
463 if(b == nil)
464 return;
465 /* current directory */
466 l = Brdline(b, '\n');
467 if(l == nil)
468 goto Return;
469 /* global fonts */
470 for(i=0; i<2; i++){
471 l = Brdline(b, '\n');
472 if(l == nil)
473 goto Return;
474 l[Blinelen(b)-1] = 0;
475 if(*l && strcmp(l, fontnames[i])!=0){
476 free(fontnames[i]);
477 fontnames[i] = estrdup(l);
478 }
479 }
480 Return:
481 Bterm(b);
482 }
483
484 int
rowload(Row * row,char * file,int initing)485 rowload(Row *row, char *file, int initing)
486 {
487 int i, j, line, percent, y, nr, nfontr, n, ns, ndumped, dumpid, x, fd;
488 Biobuf *b, *bout;
489 char *buf, *l, *t, *fontname;
490 Rune *r, rune, *fontr;
491 Column *c, *c1, *c2;
492 uint q0, q1;
493 Rectangle r1, r2;
494 Window *w;
495
496 buf = fbufalloc();
497 if(file == nil){
498 if(home == nil){
499 warning(nil, "can't find file for load: $home not defined\n");
500 goto Rescue1;
501 }
502 sprint(buf, "%s/acme.dump", home);
503 file = buf;
504 }
505 b = Bopen(file, OREAD);
506 if(b == nil){
507 warning(nil, "can't open load file %s: %r\n", file);
508 goto Rescue1;
509 }
510 /* current directory */
511 line = 0;
512 l = rdline(b, &line);
513 if(l == nil)
514 goto Rescue2;
515 l[Blinelen(b)-1] = 0;
516 if(chdir(l) < 0){
517 warning(nil, "can't chdir %s\n", l);
518 goto Rescue2;
519 }
520 /* global fonts */
521 for(i=0; i<2; i++){
522 l = rdline(b, &line);
523 if(l == nil)
524 goto Rescue2;
525 l[Blinelen(b)-1] = 0;
526 if(*l && strcmp(l, fontnames[i])!=0)
527 rfget(i, TRUE, i==0 && initing, l);
528 }
529 if(initing && row->ncol==0)
530 rowinit(row, screen->clipr);
531 l = rdline(b, &line);
532 if(l == nil)
533 goto Rescue2;
534 j = Blinelen(b)/12;
535 if(j<=0 || j>10)
536 goto Rescue2;
537 for(i=0; i<j; i++){
538 percent = atoi(l+i*12);
539 if(percent<0 || percent>=100)
540 goto Rescue2;
541 x = row->r.min.x+percent*Dx(row->r)/100;
542 if(i < row->ncol){
543 if(i == 0)
544 continue;
545 c1 = row->col[i-1];
546 c2 = row->col[i];
547 r1 = c1->r;
548 r2 = c2->r;
549 r1.max.x = x;
550 r2.min.x = x+Border;
551 if(Dx(r1) < 50 || Dx(r2) < 50)
552 continue;
553 draw(screen, Rpt(r1.min, r2.max), display->white, nil, ZP);
554 colresize(c1, r1);
555 colresize(c2, r2);
556 r2.min.x = x;
557 r2.max.x = x+Border;
558 draw(screen, r2, display->black, nil, ZP);
559 }
560 if(i >= row->ncol)
561 rowadd(row, nil, x);
562 }
563 for(;;){
564 l = rdline(b, &line);
565 if(l == nil)
566 break;
567 dumpid = 0;
568 switch(l[0]){
569 case 'e':
570 if(Blinelen(b) < 1+5*12+1)
571 goto Rescue2;
572 l = rdline(b, &line); /* ctl line; ignored */
573 if(l == nil)
574 goto Rescue2;
575 l = rdline(b, &line); /* directory */
576 if(l == nil)
577 goto Rescue2;
578 l[Blinelen(b)-1] = 0;
579 if(*l == '\0'){
580 if(home == nil)
581 r = bytetorune("./", &nr);
582 else{
583 t = emalloc(strlen(home)+1+1);
584 sprint(t, "%s/", home);
585 r = bytetorune(t, &nr);
586 free(t);
587 }
588 }else
589 r = bytetorune(l, &nr);
590 l = rdline(b, &line); /* command */
591 if(l == nil)
592 goto Rescue2;
593 t = emalloc(Blinelen(b)+1);
594 memmove(t, l, Blinelen(b));
595 run(nil, t, r, nr, TRUE, nil, nil, FALSE);
596 /* r is freed in run() */
597 continue;
598 case 'f':
599 if(Blinelen(b) < 1+5*12+1)
600 goto Rescue2;
601 fontname = l+1+5*12;
602 ndumped = -1;
603 break;
604 case 'F':
605 if(Blinelen(b) < 1+6*12+1)
606 goto Rescue2;
607 fontname = l+1+6*12;
608 ndumped = atoi(l+1+5*12+1);
609 break;
610 case 'x':
611 if(Blinelen(b) < 1+5*12+1)
612 goto Rescue2;
613 fontname = l+1+5*12;
614 ndumped = -1;
615 dumpid = atoi(l+1+1*12);
616 break;
617 default:
618 goto Rescue2;
619 }
620 l[Blinelen(b)-1] = 0;
621 fontr = nil;
622 nfontr = 0;
623 if(*fontname)
624 fontr = bytetorune(fontname, &nfontr);
625 i = atoi(l+1+0*12);
626 j = atoi(l+1+1*12);
627 q0 = atoi(l+1+2*12);
628 q1 = atoi(l+1+3*12);
629 percent = atoi(l+1+4*12);
630 if(i<0 || i>10)
631 goto Rescue2;
632 if(i > row->ncol)
633 i = row->ncol;
634 c = row->col[i];
635 y = c->r.min.y+(percent*Dy(c->r))/100;
636 if(y<c->r.min.y || y>=c->r.max.y)
637 y = -1;
638 if(dumpid == 0)
639 w = coladd(c, nil, nil, y);
640 else
641 w = coladd(c, nil, lookid(dumpid, TRUE), y);
642 if(w == nil)
643 continue;
644 w->dumpid = j;
645 l = rdline(b, &line);
646 if(l == nil)
647 goto Rescue2;
648 l[Blinelen(b)-1] = 0;
649 r = bytetorune(l+5*12, &nr);
650 ns = -1;
651 for(n=0; n<nr; n++){
652 if(r[n] == '/')
653 ns = n;
654 if(r[n] == ' ')
655 break;
656 }
657 if(dumpid == 0)
658 winsetname(w, r, n);
659 for(; n<nr; n++)
660 if(r[n] == '|')
661 break;
662 wincleartag(w);
663 textinsert(&w->tag, w->tag.file->nc, r+n+1, nr-(n+1), TRUE);
664 if(ndumped >= 0){
665 /* simplest thing is to put it in a file and load that */
666 sprint(buf, "/tmp/d%d.%.4sacme", getpid(), getuser());
667 fd = create(buf, OWRITE|ORCLOSE, 0600);
668 if(fd < 0){
669 free(r);
670 warning(nil, "can't create temp file: %r\n");
671 goto Rescue2;
672 }
673 bout = emalloc(sizeof(Biobuf));
674 Binit(bout, fd, OWRITE);
675 for(n=0; n<ndumped; n++){
676 rune = Bgetrune(b);
677 if(rune == '\n')
678 line++;
679 if(rune == (Rune)Beof){
680 free(r);
681 Bterm(bout);
682 free(bout);
683 close(fd);
684 goto Rescue2;
685 }
686 Bputrune(bout, rune);
687 }
688 Bterm(bout);
689 free(bout);
690 textload(&w->body, 0, buf, 1);
691 close(fd);
692 w->body.file->mod = TRUE;
693 for(n=0; n<w->body.file->ntext; n++)
694 w->body.file->text[n]->w->dirty = TRUE;
695 winsettag(w);
696 }else if(dumpid==0 && r[ns+1]!='+' && r[ns+1]!='-')
697 get(&w->body, nil, nil, FALSE, XXX, nil, 0);
698 if(fontr){
699 fontx(&w->body, nil, nil, 0, 0, fontr, nfontr);
700 free(fontr);
701 }
702 free(r);
703 if(q0>w->body.file->nc || q1>w->body.file->nc || q0>q1)
704 q0 = q1 = 0;
705 textshow(&w->body, q0, q1, 1);
706 w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines));
707 }
708 Bterm(b);
709 fbuffree(buf);
710 return TRUE;
711
712 Rescue2:
713 warning(nil, "bad load file %s:%d\n", file, line);
714 Bterm(b);
715 Rescue1:
716 fbuffree(buf);
717 return FALSE;
718 }
719
720 void
allwindows(void (* f)(Window *,void *),void * arg)721 allwindows(void (*f)(Window*, void*), void *arg)
722 {
723 int i, j;
724 Column *c;
725
726 for(i=0; i<row.ncol; i++){
727 c = row.col[i];
728 for(j=0; j<c->nw; j++)
729 (*f)(c->w[j], arg);
730 }
731 }
732