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 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* 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 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); 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 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 if(i == 0) 157 return; 158 p = mouse->xy; 159 if((abs(p.x-op.x)<5 && abs(p.y-op.y)<5)) 160 return; 161 if((i>0 && p.x<row->col[i-1]->r.min.x) || (i<row->ncol-1 && p.x>c->r.max.x)){ 162 /* shuffle */ 163 x = c->r.min.x; 164 rowclose(row, c, FALSE); 165 if(rowadd(row, c, p.x) == nil) /* whoops! */ 166 if(rowadd(row, c, x) == nil) /* WHOOPS! */ 167 if(rowadd(row, c, -1)==nil){ /* shit! */ 168 rowclose(row, c, TRUE); 169 return; 170 } 171 colmousebut(c); 172 return; 173 } 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 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* 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* 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* 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 winunlock(w); 275 } 276 } 277 qunlock(row); 278 return t; 279 } 280 281 int 282 rowclean(Row *row) 283 { 284 int clean; 285 int i; 286 287 clean = TRUE; 288 for(i=0; i<row->ncol; i++) 289 clean &= colclean(row->col[i]); 290 return clean; 291 } 292 293 void 294 rowdump(Row *row, char *file) 295 { 296 int i, j, fd, m, n, dumped; 297 uint q0, q1; 298 Biobuf *b; 299 char *buf, *a, *fontname; 300 Rune *r; 301 Column *c; 302 Window *w, *w1; 303 Text *t; 304 305 if(row->ncol == 0) 306 return; 307 buf = fbufalloc(); 308 if(file == nil){ 309 if(home == nil){ 310 warning(nil, "can't find file for dump: $home not defined\n"); 311 goto Rescue; 312 } 313 sprint(buf, "%s/acme.dump", home); 314 file = buf; 315 } 316 fd = create(file, OWRITE, 0600); 317 if(fd < 0){ 318 warning(nil, "can't open %s: %r\n", file); 319 goto Rescue; 320 } 321 b = emalloc(sizeof(Biobuf)); 322 Binit(b, fd, OWRITE); 323 r = fbufalloc(); 324 Bprint(b, "%s\n", wdir); 325 Bprint(b, "%s\n", fontnames[0]); 326 Bprint(b, "%s\n", fontnames[1]); 327 for(i=0; i<row->ncol; i++){ 328 c = row->col[i]; 329 Bprint(b, "%11d", 100*(c->r.min.x-row->r.min.x)/Dx(row->r)); 330 if(i == row->ncol-1) 331 Bputc(b, '\n'); 332 else 333 Bputc(b, ' '); 334 } 335 for(i=0; i<row->ncol; i++){ 336 c = row->col[i]; 337 for(j=0; j<c->nw; j++) 338 c->w[j]->body.file->dumpid = 0; 339 } 340 for(i=0; i<row->ncol; i++){ 341 c = row->col[i]; 342 for(j=0; j<c->nw; j++){ 343 w = c->w[j]; 344 wincommit(w, &w->tag); 345 t = &w->body; 346 /* windows owned by others get special treatment */ 347 if(w->nopen[QWevent] > 0) 348 if(w->dumpstr == nil) 349 continue; 350 /* zeroxes of external windows are tossed */ 351 if(t->file->ntext > 1) 352 for(n=0; n<t->file->ntext; n++){ 353 w1 = t->file->text[n]->w; 354 if(w == w1) 355 continue; 356 if(w1->nopen[QWevent]) 357 goto Continue2; 358 } 359 fontname = ""; 360 if(t->reffont->f != font) 361 fontname = t->reffont->f->name; 362 if(t->file->nname) 363 a = runetobyte(t->file->name, t->file->nname); 364 else 365 a = emalloc(1); 366 if(t->file->dumpid){ 367 dumped = FALSE; 368 Bprint(b, "x%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid, 369 w->body.q0, w->body.q1, 370 100*(w->r.min.y-c->r.min.y)/Dy(c->r), 371 fontname); 372 }else if(w->dumpstr){ 373 dumped = FALSE; 374 Bprint(b, "e%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid, 375 0, 0, 376 100*(w->r.min.y-c->r.min.y)/Dy(c->r), 377 fontname); 378 }else if(strlen(a) == 0){ /* don't save unnamed windows */ 379 free(a); 380 continue; 381 }else if((w->dirty==FALSE && access(a, 0)==0) || w->isdir){ 382 dumped = FALSE; 383 t->file->dumpid = w->id; 384 Bprint(b, "f%11d %11d %11d %11d %11d %s\n", i, w->id, 385 w->body.q0, w->body.q1, 386 100*(w->r.min.y-c->r.min.y)/Dy(c->r), 387 fontname); 388 }else{ 389 dumped = TRUE; 390 t->file->dumpid = w->id; 391 Bprint(b, "F%11d %11d %11d %11d %11d %11d %s\n", i, j, 392 w->body.q0, w->body.q1, 393 100*(w->r.min.y-c->r.min.y)/Dy(c->r), 394 w->body.file->nc, fontname); 395 } 396 free(a); 397 winctlprint(w, buf, 0); 398 Bwrite(b, buf, strlen(buf)); 399 m = min(RBUFSIZE, w->tag.file->nc); 400 bufread(w->tag.file, 0, r, m); 401 n = 0; 402 while(n<m && r[n]!='\n') 403 n++; 404 r[n++] = '\n'; 405 Bprint(b, "%.*S", n, r); 406 if(dumped){ 407 q0 = 0; 408 q1 = t->file->nc; 409 while(q0 < q1){ 410 n = q1 - q0; 411 if(n > BUFSIZE/UTFmax) 412 n = BUFSIZE/UTFmax; 413 bufread(t->file, q0, r, n); 414 Bprint(b, "%.*S", n, r); 415 q0 += n; 416 } 417 } 418 if(w->dumpstr){ 419 if(w->dumpdir) 420 Bprint(b, "%s\n%s\n", w->dumpdir, w->dumpstr); 421 else 422 Bprint(b, "\n%s\n", w->dumpstr); 423 } 424 Continue2:; 425 } 426 } 427 Bterm(b); 428 close(fd); 429 free(b); 430 fbuffree(r); 431 432 Rescue: 433 fbuffree(buf); 434 } 435 436 static 437 char* 438 rdline(Biobuf *b, int *linep) 439 { 440 char *l; 441 442 l = Brdline(b, '\n'); 443 if(l) 444 (*linep)++; 445 return l; 446 } 447 448 /* 449 * Get font names from load file so we don't load fonts we won't use 450 */ 451 void 452 rowloadfonts(char *file) 453 { 454 int i; 455 Biobuf *b; 456 char *l; 457 458 b = Bopen(file, OREAD); 459 if(b == nil) 460 return; 461 /* current directory */ 462 l = Brdline(b, '\n'); 463 if(l == nil) 464 goto Return; 465 /* global fonts */ 466 for(i=0; i<2; i++){ 467 l = Brdline(b, '\n'); 468 if(l == nil) 469 goto Return; 470 l[Blinelen(b)-1] = 0; 471 if(*l && strcmp(l, fontnames[i])!=0) 472 fontnames[i] = estrdup(l); 473 } 474 Return: 475 Bterm(b); 476 } 477 478 void 479 rowload(Row *row, char *file, int initing) 480 { 481 int i, j, line, percent, y, nr, nfontr, n, ns, ndumped, dumpid, x, fd; 482 Biobuf *b, *bout; 483 char *buf, *l, *t, *fontname; 484 Rune *r, rune, *fontr; 485 Column *c, *c1, *c2; 486 uint q0, q1; 487 Rectangle r1, r2; 488 Window *w; 489 490 buf = fbufalloc(); 491 if(file == nil){ 492 if(home == nil){ 493 warning(nil, "can't find file for load: $home not defined\n"); 494 goto Rescue1; 495 } 496 sprint(buf, "%s/acme.dump", home); 497 file = buf; 498 } 499 b = Bopen(file, OREAD); 500 if(b == nil){ 501 warning(nil, "can't open load file %s: %r\n", file); 502 goto Rescue1; 503 } 504 /* current directory */ 505 line = 0; 506 l = rdline(b, &line); 507 if(l == nil) 508 goto Rescue2; 509 l[Blinelen(b)-1] = 0; 510 if(chdir(l) < 0){ 511 warning(nil, "can't chdir %s\n", l); 512 goto Rescue2; 513 } 514 /* global fonts */ 515 for(i=0; i<2; i++){ 516 l = rdline(b, &line); 517 if(l == nil) 518 goto Rescue2; 519 l[Blinelen(b)-1] = 0; 520 if(*l && strcmp(l, fontnames[i])!=0) 521 rfget(i, TRUE, i==0 && initing, estrdup(l)); 522 } 523 if(initing && row->ncol==0) 524 rowinit(row, screen->clipr); 525 l = rdline(b, &line); 526 if(l == nil) 527 goto Rescue2; 528 j = Blinelen(b)/12; 529 if(j<=0 || j>10) 530 goto Rescue2; 531 for(i=0; i<j; i++){ 532 percent = atoi(l+i*12); 533 if(percent<0 || percent>=100) 534 goto Rescue2; 535 x = row->r.min.x+percent*Dx(row->r)/100; 536 if(i < row->ncol){ 537 if(i == 0) 538 continue; 539 c1 = row->col[i-1]; 540 c2 = row->col[i]; 541 r1 = c1->r; 542 r2 = c2->r; 543 r1.max.x = x; 544 r2.min.x = x+Border; 545 if(Dx(r1) < 50 || Dx(r2) < 50) 546 continue; 547 draw(screen, Rpt(r1.min, r2.max), display->white, nil, ZP); 548 colresize(c1, r1); 549 colresize(c2, r2); 550 r2.min.x = x; 551 r2.max.x = x+Border; 552 draw(screen, r2, display->black, nil, ZP); 553 } 554 if(i >= row->ncol) 555 rowadd(row, nil, x); 556 } 557 for(;;){ 558 l = rdline(b, &line); 559 if(l == nil) 560 break; 561 dumpid = 0; 562 switch(l[0]){ 563 case 'e': 564 if(Blinelen(b) < 1+5*12+1) 565 goto Rescue2; 566 l = rdline(b, &line); /* ctl line; ignored */ 567 if(l == nil) 568 goto Rescue2; 569 l = rdline(b, &line); /* directory */ 570 if(l == nil) 571 goto Rescue2; 572 l[Blinelen(b)-1] = 0; 573 if(*l == '\0'){ 574 if(home == nil) 575 r = bytetorune("./", &nr); 576 else{ 577 t = emalloc(strlen(home)+1+1); 578 sprint(t, "%s/", home); 579 r = bytetorune(t, &nr); 580 free(t); 581 } 582 }else 583 r = bytetorune(l, &nr); 584 l = rdline(b, &line); /* command */ 585 if(l == nil) 586 goto Rescue2; 587 t = emalloc(Blinelen(b)+1); 588 memmove(t, l, Blinelen(b)); 589 run(nil, t, r, nr, TRUE, nil, nil, FALSE); 590 /* r is freed in run() */ 591 continue; 592 case 'f': 593 if(Blinelen(b) < 1+5*12+1) 594 goto Rescue2; 595 fontname = l+1+5*12; 596 ndumped = -1; 597 break; 598 case 'F': 599 if(Blinelen(b) < 1+6*12+1) 600 goto Rescue2; 601 fontname = l+1+6*12; 602 ndumped = atoi(l+1+5*12+1); 603 break; 604 case 'x': 605 if(Blinelen(b) < 1+5*12+1) 606 goto Rescue2; 607 fontname = l+1+5*12; 608 ndumped = -1; 609 dumpid = atoi(l+1+1*12); 610 break; 611 default: 612 goto Rescue2; 613 } 614 l[Blinelen(b)-1] = 0; 615 fontr = nil; 616 nfontr = 0; 617 if(*fontname) 618 fontr = bytetorune(fontname, &nfontr); 619 i = atoi(l+1+0*12); 620 j = atoi(l+1+1*12); 621 q0 = atoi(l+1+2*12); 622 q1 = atoi(l+1+3*12); 623 percent = atoi(l+1+4*12); 624 if(i<0 || i>10) 625 goto Rescue2; 626 if(i > row->ncol) 627 i = row->ncol; 628 c = row->col[i]; 629 y = c->r.min.y+(percent*Dy(c->r))/100; 630 if(y<c->r.min.y || y>=c->r.max.y) 631 y = -1; 632 if(dumpid == 0) 633 w = coladd(c, nil, nil, y); 634 else 635 w = coladd(c, nil, lookid(dumpid, TRUE), y); 636 if(w == nil) 637 continue; 638 w->dumpid = j; 639 l = rdline(b, &line); 640 if(l == nil) 641 goto Rescue2; 642 l[Blinelen(b)-1] = 0; 643 r = bytetorune(l+5*12, &nr); 644 ns = -1; 645 for(n=0; n<nr; n++){ 646 if(r[n] == '/') 647 ns = n; 648 if(r[n] == ' ') 649 break; 650 } 651 if(dumpid == 0) 652 winsetname(w, r, n); 653 for(; n<nr; n++) 654 if(r[n] == '|') 655 break; 656 wincleartag(w); 657 textinsert(&w->tag, w->tag.file->nc, r+n+1, nr-(n+1), TRUE); 658 free(r); 659 if(ndumped >= 0){ 660 /* simplest thing is to put it in a file and load that */ 661 sprint(buf, "/tmp/d%d.%.4sacme", getpid(), getuser()); 662 fd = create(buf, OWRITE|ORCLOSE, 0600); 663 if(fd < 0){ 664 warning(nil, "can't create temp file: %r\n"); 665 goto Rescue2; 666 } 667 bout = emalloc(sizeof(Biobuf)); 668 Binit(bout, fd, OWRITE); 669 for(n=0; n<ndumped; n++){ 670 rune = Bgetrune(b); 671 if(rune == '\n') 672 line++; 673 if(rune == Beof){ 674 Bterm(bout); 675 free(bout); 676 close(fd); 677 goto Rescue2; 678 } 679 Bputrune(bout, rune); 680 } 681 Bterm(bout); 682 free(bout); 683 textload(&w->body, 0, buf, 1); 684 close(fd); 685 w->body.file->mod = TRUE; 686 for(n=0; n<w->body.file->ntext; n++) 687 w->body.file->text[n]->w->dirty = TRUE; 688 winsettag(w); 689 }else if(dumpid==0 && r[ns+1]!='+' && r[ns+1]!='-') 690 get(&w->body, nil, nil, FALSE, XXX, nil, 0); 691 if(fontr){ 692 fontx(&w->body, nil, nil, 0, 0, fontr, nfontr); 693 free(fontr); 694 } 695 if(q0>w->body.file->nc || q1>w->body.file->nc || q0>q1) 696 q0 = q1 = 0; 697 textshow(&w->body, q0, q1, 1); 698 w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines)); 699 } 700 Bterm(b); 701 702 Rescue1: 703 fbuffree(buf); 704 return; 705 706 Rescue2: 707 warning(nil, "bad load file %s:%d\n", file, line); 708 Bterm(b); 709 goto Rescue1; 710 } 711 712 void 713 allwindows(void (*f)(Window*, void*), void *arg) 714 { 715 int i, j; 716 Column *c; 717 718 for(i=0; i<row.ncol; i++){ 719 c = row.col[i]; 720 for(j=0; j<c->nw; j++) 721 (*f)(c->w[j], arg); 722 } 723 } 724