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