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