cols.c (12719B)
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 <plumb.h> 11 #include <libsec.h> 12 #include "dat.h" 13 #include "fns.h" 14 15 static Rune Lheader[] = { 16 'N', 'e', 'w', ' ', 17 'C', 'u', 't', ' ', 18 'P', 'a', 's', 't', 'e', ' ', 19 'S', 'n', 'a', 'r', 'f', ' ', 20 'S', 'o', 'r', 't', ' ', 21 'Z', 'e', 'r', 'o', 'x', ' ', 22 'D', 'e', 'l', 'c', 'o', 'l', ' ', 23 'w', 'i', 'n', ' ', 24 0 25 }; 26 27 void 28 colinit(Column *c, Rectangle r) 29 { 30 Rectangle r1; 31 Text *t; 32 33 draw(screen, r, display->white, nil, ZP); 34 c->r = r; 35 c->w = nil; 36 c->nw = 0; 37 t = &c->tag; 38 t->w = nil; 39 t->col = c; 40 r1 = r; 41 r1.max.y = r1.min.y + font->height; 42 textinit(t, fileaddtext(nil, t), r1, &reffont, tagcols); 43 t->what = Columntag; 44 r1.min.y = r1.max.y; 45 r1.max.y += Border; 46 draw(screen, r1, display->black, nil, ZP); 47 //textinsert(t, 0, Lheader, 38, TRUE); 48 textinsert(t, 0, Lheader, sizeof(Lheader) / sizeof(Rune), TRUE); 49 textsetselect(t, t->file->b.nc, t->file->b.nc); 50 draw(screen, t->scrollr, colbutton, nil, colbutton->r.min); 51 c->safe = TRUE; 52 } 53 54 Window* 55 coladd(Column *c, Window *w, Window *clone, int y) 56 { 57 Rectangle r, r1; 58 Window *v; 59 int i, j, minht, ymax, buggered; 60 61 v = nil; 62 r = c->r; 63 r.min.y = c->tag.fr.r.max.y+Border; 64 if(y<r.min.y && c->nw>0){ /* steal half of last window by default */ 65 v = c->w[c->nw-1]; 66 y = v->body.fr.r.min.y+Dy(v->body.fr.r)/2; 67 } 68 /* look for window we'll land on */ 69 for(i=0; i<c->nw; i++){ 70 v = c->w[i]; 71 if(y < v->r.max.y) 72 break; 73 } 74 buggered = 0; 75 if(c->nw > 0){ 76 if(i < c->nw) 77 i++; /* new window will go after v */ 78 /* 79 * if landing window (v) is too small, grow it first. 80 */ 81 minht = v->tag.fr.font->height+Border+1; 82 j = 0; 83 while(!c->safe || v->body.fr.maxlines<=3 || Dy(v->body.all) <= minht){ 84 if(++j > 10){ 85 buggered = 1; /* too many windows in column */ 86 break; 87 } 88 colgrow(c, v, 1); 89 } 90 91 /* 92 * figure out where to split v to make room for w 93 */ 94 95 /* new window stops where next window begins */ 96 if(i < c->nw) 97 ymax = c->w[i]->r.min.y-Border; 98 else 99 ymax = c->r.max.y; 100 101 /* new window must start after v's tag ends */ 102 y = max(y, v->tagtop.max.y+Border); 103 104 /* new window must start early enough to end before ymax */ 105 y = min(y, ymax - minht); 106 107 /* if y is too small, too many windows in column */ 108 if(y < v->tagtop.max.y+Border) 109 buggered = 1; 110 111 /* 112 * resize & redraw v 113 */ 114 r = v->r; 115 r.max.y = ymax; 116 draw(screen, r, textcols[BACK], nil, ZP); 117 r1 = r; 118 y = min(y, ymax-(v->tag.fr.font->height*v->taglines+v->body.fr.font->height+Border+1)); 119 r1.max.y = min(y, v->body.fr.r.min.y+v->body.fr.nlines*v->body.fr.font->height); 120 r1.min.y = winresize(v, r1, FALSE, FALSE); 121 r1.max.y = r1.min.y+Border; 122 draw(screen, r1, display->black, nil, ZP); 123 124 /* 125 * leave r with w's coordinates 126 */ 127 r.min.y = r1.max.y; 128 } 129 if(w == nil){ 130 w = emalloc(sizeof(Window)); 131 w->col = c; 132 draw(screen, r, textcols[BACK], nil, ZP); 133 wininit(w, clone, r); 134 }else{ 135 w->col = c; 136 winresize(w, r, FALSE, TRUE); 137 } 138 w->tag.col = c; 139 w->tag.row = c->row; 140 w->body.col = c; 141 w->body.row = c->row; 142 c->w = realloc(c->w, (c->nw+1)*sizeof(Window*)); 143 memmove(c->w+i+1, c->w+i, (c->nw-i)*sizeof(Window*)); 144 c->nw++; 145 c->w[i] = w; 146 c->safe = TRUE; 147 148 /* if there were too many windows, redraw the whole column */ 149 if(buggered) 150 colresize(c, c->r); 151 152 savemouse(w); 153 /* near the button, but in the body */ 154 /* don't move the mouse to the new window if a mouse button is depressed */ 155 if(!mousectl->m.buttons) 156 moveto(mousectl, addpt(w->tag.scrollr.max, Pt(3, 3))); 157 158 barttext = &w->body; 159 return w; 160 } 161 162 void 163 colclose(Column *c, Window *w, int dofree) 164 { 165 Rectangle r; 166 int i, didmouse, up; 167 168 /* w is locked */ 169 if(!c->safe) 170 colgrow(c, w, 1); 171 for(i=0; i<c->nw; i++) 172 if(c->w[i] == w) 173 goto Found; 174 error("can't find window"); 175 Found: 176 r = w->r; 177 w->tag.col = nil; 178 w->body.col = nil; 179 w->col = nil; 180 didmouse = restoremouse(w); 181 if(dofree){ 182 windelete(w); 183 winclose(w); 184 } 185 c->nw--; 186 memmove(c->w+i, c->w+i+1, (c->nw-i)*sizeof(Window*)); 187 c->w = realloc(c->w, c->nw*sizeof(Window*)); 188 if(c->nw == 0){ 189 draw(screen, r, display->white, nil, ZP); 190 return; 191 } 192 up = 0; 193 if(i == c->nw){ /* extend last window down */ 194 w = c->w[i-1]; 195 r.min.y = w->r.min.y; 196 r.max.y = c->r.max.y; 197 }else{ /* extend next window up */ 198 up = 1; 199 w = c->w[i]; 200 r.max.y = w->r.max.y; 201 } 202 draw(screen, r, textcols[BACK], nil, ZP); 203 if(c->safe) { 204 if(!didmouse && up) 205 w->showdel = TRUE; 206 winresize(w, r, FALSE, TRUE); 207 if(!didmouse && up) 208 movetodel(w); 209 } 210 } 211 212 void 213 colcloseall(Column *c) 214 { 215 int i; 216 Window *w; 217 218 if(c == activecol) 219 activecol = nil; 220 textclose(&c->tag); 221 for(i=0; i<c->nw; i++){ 222 w = c->w[i]; 223 winclose(w); 224 } 225 c->nw = 0; 226 free(c->w); 227 free(c); 228 clearmouse(); 229 } 230 231 void 232 colmousebut(Column *c) 233 { 234 moveto(mousectl, divpt(addpt(c->tag.scrollr.min, c->tag.scrollr.max), 2)); 235 } 236 237 void 238 colresize(Column *c, Rectangle r) 239 { 240 int i, old, new; 241 Rectangle r1, r2; 242 Window *w; 243 244 clearmouse(); 245 r1 = r; 246 r1.max.y = r1.min.y + c->tag.fr.font->height; 247 textresize(&c->tag, r1, TRUE); 248 draw(screen, c->tag.scrollr, colbutton, nil, colbutton->r.min); 249 r1.min.y = r1.max.y; 250 r1.max.y += Border; 251 draw(screen, r1, display->black, nil, ZP); 252 r1.max.y = r.max.y; 253 new = Dy(r) - c->nw*(Border + font->height); 254 old = Dy(c->r) - c->nw*(Border + font->height); 255 for(i=0; i<c->nw; i++){ 256 w = c->w[i]; 257 w->maxlines = 0; 258 if(i == c->nw-1) 259 r1.max.y = r.max.y; 260 else{ 261 r1.max.y = r1.min.y; 262 if(new > 0 && old > 0 && Dy(w->r) > Border+font->height){ 263 r1.max.y += (Dy(w->r)-Border-font->height)*new/old + Border + font->height; 264 } 265 } 266 r1.max.y = max(r1.max.y, r1.min.y + Border+font->height); 267 r2 = r1; 268 r2.max.y = r2.min.y+Border; 269 draw(screen, r2, display->black, nil, ZP); 270 r1.min.y = r2.max.y; 271 r1.min.y = winresize(w, r1, FALSE, i==c->nw-1); 272 } 273 c->r = r; 274 } 275 276 static 277 int 278 colcmp(const void *a, const void *b) 279 { 280 Rune *r1, *r2; 281 int i, nr1, nr2; 282 283 r1 = (*(Window**)a)->body.file->name; 284 nr1 = (*(Window**)a)->body.file->nname; 285 r2 = (*(Window**)b)->body.file->name; 286 nr2 = (*(Window**)b)->body.file->nname; 287 for(i=0; i<nr1 && i<nr2; i++){ 288 if(*r1 != *r2) 289 return *r1-*r2; 290 r1++; 291 r2++; 292 } 293 return nr1-nr2; 294 } 295 296 void 297 colsort(Column *c) 298 { 299 int i, y; 300 Rectangle r, r1, *rp; 301 Window **wp, *w; 302 303 if(c->nw == 0) 304 return; 305 clearmouse(); 306 rp = emalloc(c->nw*sizeof(Rectangle)); 307 wp = emalloc(c->nw*sizeof(Window*)); 308 memmove(wp, c->w, c->nw*sizeof(Window*)); 309 qsort(wp, c->nw, sizeof(Window*), colcmp); 310 for(i=0; i<c->nw; i++) 311 rp[i] = wp[i]->r; 312 r = c->r; 313 r.min.y = c->tag.fr.r.max.y; 314 draw(screen, r, textcols[BACK], nil, ZP); 315 y = r.min.y; 316 for(i=0; i<c->nw; i++){ 317 w = wp[i]; 318 r.min.y = y; 319 if(i == c->nw-1) 320 r.max.y = c->r.max.y; 321 else 322 r.max.y = r.min.y+Dy(w->r)+Border; 323 r1 = r; 324 r1.max.y = r1.min.y+Border; 325 draw(screen, r1, display->black, nil, ZP); 326 r.min.y = r1.max.y; 327 y = winresize(w, r, FALSE, i==c->nw-1); 328 } 329 free(rp); 330 free(c->w); 331 c->w = wp; 332 } 333 334 void 335 colgrow(Column *c, Window *w, int but) 336 { 337 Rectangle r, cr; 338 int i, j, k, l, y1, y2, *nl, *ny, tot, nnl, onl, dnl, h; 339 Window *v; 340 341 for(i=0; i<c->nw; i++) 342 if(c->w[i] == w) 343 goto Found; 344 error("can't find window"); 345 346 Found: 347 cr = c->r; 348 if(but < 0){ /* make sure window fills its own space properly */ 349 r = w->r; 350 if(i==c->nw-1 || c->safe==FALSE) 351 r.max.y = cr.max.y; 352 else 353 r.max.y = c->w[i+1]->r.min.y - Border; 354 winresize(w, r, FALSE, TRUE); 355 return; 356 } 357 cr.min.y = c->w[0]->r.min.y; 358 if(but == 3){ /* full size */ 359 if(i != 0){ 360 v = c->w[0]; 361 c->w[0] = w; 362 c->w[i] = v; 363 } 364 draw(screen, cr, textcols[BACK], nil, ZP); 365 winresize(w, cr, FALSE, TRUE); 366 for(i=1; i<c->nw; i++) 367 c->w[i]->body.fr.maxlines = 0; 368 c->safe = FALSE; 369 return; 370 } 371 /* store old #lines for each window */ 372 onl = w->body.fr.maxlines; 373 nl = emalloc(c->nw * sizeof(int)); 374 ny = emalloc(c->nw * sizeof(int)); 375 tot = 0; 376 for(j=0; j<c->nw; j++){ 377 l = c->w[j]->taglines-1 + c->w[j]->body.fr.maxlines; 378 nl[j] = l; 379 tot += l; 380 } 381 /* approximate new #lines for this window */ 382 if(but == 2){ /* as big as can be */ 383 memset(nl, 0, c->nw * sizeof(int)); 384 goto Pack; 385 } 386 nnl = min(onl + max(min(5, w->taglines-1+w->maxlines), onl/2), tot); 387 if(nnl < w->taglines-1+w->maxlines) 388 nnl = (w->taglines-1+w->maxlines + nnl)/2; 389 if(nnl == 0) 390 nnl = 2; 391 dnl = nnl - onl; 392 /* compute new #lines for each window */ 393 for(k=1; k<c->nw; k++){ 394 /* prune from later window */ 395 j = i+k; 396 if(j<c->nw && nl[j]){ 397 l = min(dnl, max(1, nl[j]/2)); 398 nl[j] -= l; 399 nl[i] += l; 400 dnl -= l; 401 } 402 /* prune from earlier window */ 403 j = i-k; 404 if(j>=0 && nl[j]){ 405 l = min(dnl, max(1, nl[j]/2)); 406 nl[j] -= l; 407 nl[i] += l; 408 dnl -= l; 409 } 410 } 411 Pack: 412 /* pack everyone above */ 413 y1 = cr.min.y; 414 for(j=0; j<i; j++){ 415 v = c->w[j]; 416 r = v->r; 417 r.min.y = y1; 418 r.max.y = y1+Dy(v->tagtop); 419 if(nl[j]) 420 r.max.y += 1 + nl[j]*v->body.fr.font->height; 421 r.min.y = winresize(v, r, c->safe, FALSE); 422 r.max.y = r.min.y + Border; 423 draw(screen, r, display->black, nil, ZP); 424 y1 = r.max.y; 425 } 426 /* scan to see new size of everyone below */ 427 y2 = c->r.max.y; 428 for(j=c->nw-1; j>i; j--){ 429 v = c->w[j]; 430 r = v->r; 431 r.min.y = y2-Dy(v->tagtop); 432 if(nl[j]) 433 r.min.y -= 1 + nl[j]*v->body.fr.font->height; 434 r.min.y -= Border; 435 ny[j] = r.min.y; 436 y2 = r.min.y; 437 } 438 /* compute new size of window */ 439 r = w->r; 440 r.min.y = y1; 441 r.max.y = y2; 442 h = w->body.fr.font->height; 443 if(Dy(r) < Dy(w->tagtop)+1+h+Border) 444 r.max.y = r.min.y + Dy(w->tagtop)+1+h+Border; 445 /* draw window */ 446 r.max.y = winresize(w, r, c->safe, TRUE); 447 if(i < c->nw-1){ 448 r.min.y = r.max.y; 449 r.max.y += Border; 450 draw(screen, r, display->black, nil, ZP); 451 for(j=i+1; j<c->nw; j++) 452 ny[j] -= (y2-r.max.y); 453 } 454 /* pack everyone below */ 455 y1 = r.max.y; 456 for(j=i+1; j<c->nw; j++){ 457 v = c->w[j]; 458 r = v->r; 459 r.min.y = y1; 460 r.max.y = y1+Dy(v->tagtop); 461 if(nl[j]) 462 r.max.y += 1 + nl[j]*v->body.fr.font->height; 463 y1 = winresize(v, r, c->safe, j==c->nw-1); 464 if(j < c->nw-1){ /* no border on last window */ 465 r.min.y = y1; 466 r.max.y += Border; 467 draw(screen, r, display->black, nil, ZP); 468 y1 = r.max.y; 469 } 470 } 471 free(nl); 472 free(ny); 473 c->safe = TRUE; 474 winmousebut(w); 475 } 476 477 void 478 coldragwin(Column *c, Window *w, int but) 479 { 480 Rectangle r; 481 int i, b; 482 Point p, op; 483 Window *v; 484 Column *nc; 485 486 clearmouse(); 487 setcursor2(mousectl, &boxcursor, &boxcursor2); 488 b = mouse->buttons; 489 op = mouse->xy; 490 while(mouse->buttons == b) 491 readmouse(mousectl); 492 setcursor(mousectl, nil); 493 if(mouse->buttons){ 494 while(mouse->buttons) 495 readmouse(mousectl); 496 return; 497 } 498 499 for(i=0; i<c->nw; i++) 500 if(c->w[i] == w) 501 goto Found; 502 error("can't find window"); 503 504 Found: 505 if(w->tagexpand) /* force recomputation of window tag size */ 506 w->taglines = 1; 507 p = mouse->xy; 508 if(abs(p.x-op.x)<5 && abs(p.y-op.y)<5){ 509 colgrow(c, w, but); 510 winmousebut(w); 511 return; 512 } 513 /* is it a flick to the right? */ 514 if(abs(p.y-op.y)<10 && p.x>op.x+30 && rowwhichcol(c->row, p)==c) 515 p.x = op.x+Dx(w->r); /* yes: toss to next column */ 516 nc = rowwhichcol(c->row, p); 517 if(nc!=nil && nc!=c){ 518 colclose(c, w, FALSE); 519 coladd(nc, w, nil, p.y); 520 winmousebut(w); 521 return; 522 } 523 if(i==0 && c->nw==1) 524 return; /* can't do it */ 525 if((i>0 && p.y<c->w[i-1]->r.min.y) || (i<c->nw-1 && p.y>w->r.max.y) 526 || (i==0 && p.y>w->r.max.y)){ 527 /* shuffle */ 528 colclose(c, w, FALSE); 529 coladd(c, w, nil, p.y); 530 winmousebut(w); 531 return; 532 } 533 if(i == 0) 534 return; 535 v = c->w[i-1]; 536 if(p.y < v->tagtop.max.y) 537 p.y = v->tagtop.max.y; 538 if(p.y > w->r.max.y-Dy(w->tagtop)-Border) 539 p.y = w->r.max.y-Dy(w->tagtop)-Border; 540 r = v->r; 541 r.max.y = p.y; 542 if(r.max.y > v->body.fr.r.min.y){ 543 r.max.y -= (r.max.y-v->body.fr.r.min.y)%v->body.fr.font->height; 544 if(v->body.fr.r.min.y == v->body.fr.r.max.y) 545 r.max.y++; 546 } 547 r.min.y = winresize(v, r, c->safe, FALSE); 548 r.max.y = r.min.y+Border; 549 draw(screen, r, display->black, nil, ZP); 550 r.min.y = r.max.y; 551 if(i == c->nw-1) 552 r.max.y = c->r.max.y; 553 else 554 r.max.y = c->w[i+1]->r.min.y-Border; 555 winresize(w, r, c->safe, TRUE); 556 c->safe = TRUE; 557 winmousebut(w); 558 } 559 560 Text* 561 colwhich(Column *c, Point p) 562 { 563 int i; 564 Window *w; 565 566 if(!ptinrect(p, c->r)) 567 return nil; 568 if(ptinrect(p, c->tag.all)) 569 return &c->tag; 570 for(i=0; i<c->nw; i++){ 571 w = c->w[i]; 572 if(ptinrect(p, w->r)){ 573 if(ptinrect(p, w->tagtop) || ptinrect(p, w->tag.all)) 574 return &w->tag; 575 /* exclude partial line at bottom */ 576 if(p.x >= w->body.scrollr.max.x && p.y >= w->body.fr.r.max.y) 577 return nil; 578 return &w->body; 579 } 580 } 581 return nil; 582 } 583 584 int 585 colclean(Column *c) 586 { 587 int i, clean; 588 589 clean = TRUE; 590 for(i=0; i<c->nw; i++) 591 clean &= winclean(c->w[i], TRUE); 592 return clean; 593 }