column.c (11606B)
1 /* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> 2 * Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail> 3 * See LICENSE file for license details. 4 */ 5 #include "dat.h" 6 #include <math.h> 7 #include <strings.h> 8 #include "fns.h" 9 10 static void column_resizeframe_h(Frame*, Rectangle); 11 12 char *modes[] = { 13 [Coldefault] = "default", 14 [Colstack] = "stack", 15 [Colmax] = "max", 16 }; 17 18 bool 19 column_setmode(Area *a, const char *mode) { 20 char *str, *tok; 21 int add, old; 22 23 /* 24 * The mapping between the current internal 25 * representation and the external interface 26 * is currently a bit complex. That will probably 27 * change. 28 */ 29 30 str = freelater(estrdup(mode)); 31 old = '+'; 32 while((tok = mask(&str, &add, &old))) { 33 if(!strcmp(tok, "max")) { 34 if(add == '\0' || add == '+') 35 a->max = true; 36 else if(add == '-') 37 a->max = false; 38 else 39 a->max = !a->max; 40 }else 41 if(!strcmp(tok, "stack")) { 42 if(add == '\0' || add == '+') 43 a->mode = Colstack; 44 else if(add == '-') 45 a->mode = Coldefault; 46 else 47 a->mode = a->mode == Colstack ? Coldefault : Colstack; 48 }else 49 if(!strcmp(tok, "default")) { 50 if(add == '\0' || add == '+') { 51 a->mode = Coldefault; 52 column_arrange(a, true); 53 }else if(add == '-') 54 a->mode = Colstack; 55 else 56 a->mode = a->mode == Coldefault ? Colstack : Coldefault; 57 }else 58 return false; 59 } 60 return true; 61 } 62 63 char* 64 column_getmode(Area *a) { 65 return sxprint("%s%cmax", a->mode == Colstack ? "stack" : "default", 66 a->max ? '+' : '-'); 67 } 68 69 int 70 column_minwidth(void) 71 { 72 return 4 * labelh(def.font); 73 } 74 75 static void 76 columns_update(View *v) { 77 Area *a; 78 Frame *f; 79 int s; 80 81 foreach_frame(v, s, a, f) { 82 f->screen = s; 83 f->column = area_idx(a); 84 } 85 } 86 87 Area* 88 column_new(View *v, Area *pos, int scrn, uint w) { 89 Area *a; 90 91 assert(!pos || !pos->floating && pos->screen == scrn); 92 a = area_create(v, pos, scrn, w); 93 return a; 94 #if 0 95 if(!a) 96 return nil; 97 98 view_arrange(v); 99 columns_update(v); 100 view_update(v); 101 #endif 102 } 103 104 void 105 column_insert(Area *a, Frame *f, Frame *pos) { 106 107 f->area = a; 108 if(f->client->floating == On) 109 f->client->floating = Off; 110 f->screen = a->screen; 111 f->column = area_idx(a); 112 frame_insert(f, pos); 113 if(a->sel == nil) 114 area_setsel(a, f); 115 } 116 117 void 118 column_destroy(Area *a) { 119 View *v; 120 121 v = a->view; 122 area_destroy(a); 123 columns_update(v); 124 } 125 126 void 127 column_attach(Area *a, Frame *f) { 128 Frame *first; 129 int nframe, dy, h; 130 131 f->colr = a->r; 132 133 if(a->sel) { 134 stack_info(a->sel, &first, nil, &dy, &nframe); 135 h = dy / (nframe+1); 136 f->colr.max.y = f->colr.min.y + h; 137 stack_scale(first, dy - h); 138 } 139 140 column_insert(a, f, a->sel); 141 column_arrange(a, false); 142 } 143 144 void 145 column_detach(Frame *f) { 146 Frame *first; 147 Area *a; 148 int dy; 149 150 a = f->area; 151 stack_info(f, &first, nil, &dy, nil); 152 if(first && first == f) 153 first = f->anext; 154 column_remove(f); 155 if(a->frame) { 156 if(first) 157 stack_scale(first, dy); 158 column_arrange(a, false); 159 }else if(a->view->areas[a->screen]->next) 160 column_destroy(a); 161 } 162 163 static void column_scale(Area*); 164 165 void 166 column_attachrect(Area *a, Frame *f, Rectangle r) { 167 Frame *fp, *pos; 168 169 pos = nil; 170 for(fp=a->frame; fp; pos=fp, fp=fp->anext) { 171 if(r.max.y < fp->r.min.y || r.min.y > fp->r.max.y) 172 continue; 173 /* 174 before = fp->r.min.y - r.min.y; 175 after = -fp->r.max.y + r.max.y; 176 */ 177 } 178 column_insert(a, f, pos); 179 column_resizeframe_h(f, r); 180 column_scale(a); 181 } 182 183 void 184 column_remove(Frame *f) { 185 Frame *pr; 186 Area *a; 187 188 a = f->area; 189 pr = f->aprev; 190 191 frame_remove(f); 192 193 f->area = nil; 194 if(a->sel == f) { 195 if(pr == nil) 196 pr = a->frame; 197 if(pr && pr->collapsed) 198 if(pr->anext && !pr->anext->collapsed) 199 pr = pr->anext; 200 else 201 pr->collapsed = false; 202 a->sel = nil; 203 area_setsel(a, pr); 204 } 205 } 206 207 static int 208 column_surplus(Area *a) { 209 Frame *f; 210 int surplus; 211 212 surplus = Dy(a->r); 213 for(f=a->frame; f; f=f->anext) 214 surplus -= Dy(f->r); 215 return surplus; 216 } 217 218 static void 219 column_fit(Area *a, uint *n_colp, uint *n_uncolp) { 220 Frame *f, **fp; 221 uint minh, dy; 222 uint n_col, n_uncol; 223 uint col_h, uncol_h; 224 int surplus, i, j; 225 226 /* The minimum heights of collapsed and uncollpsed frames. 227 */ 228 minh = labelh(def.font); 229 col_h = labelh(def.font); 230 uncol_h = minh + col_h + 1; 231 if(a->max && !resizing) 232 col_h = 0; 233 234 /* Count collapsed and uncollapsed frames. */ 235 n_col = 0; 236 n_uncol = 0; 237 for(f=a->frame; f; f=f->anext) { 238 frame_resize(f, f->colr); 239 if(f->collapsed) 240 n_col++; 241 else 242 n_uncol++; 243 } 244 245 if(n_uncol == 0) { 246 n_uncol++; 247 n_col--; 248 (a->sel ? a->sel : a->frame)->collapsed = false; 249 } 250 251 /* FIXME: Kludge. See frame_attachrect. */ 252 dy = Dy(a->view->r[a->screen]) - Dy(a->r); 253 minh = col_h * (n_col + n_uncol - 1) + uncol_h; 254 if(dy && Dy(a->r) < minh) 255 a->r.max.y += min(dy, minh - Dy(a->r)); 256 257 surplus = Dy(a->r) 258 - (n_col * col_h) 259 - (n_uncol * uncol_h); 260 261 /* Collapse until there is room */ 262 if(surplus < 0) { 263 i = ceil(-1.F * surplus / (uncol_h - col_h)); 264 if(i >= n_uncol) 265 i = n_uncol - 1; 266 n_uncol -= i; 267 n_col += i; 268 surplus += i * (uncol_h - col_h); 269 } 270 /* Push to the floating layer until there is room */ 271 if(surplus < 0) { 272 i = ceil(-1.F * surplus / col_h); 273 if(i > n_col) 274 i = n_col; 275 n_col -= i; 276 surplus += i * col_h; 277 } 278 279 /* Decide which to collapse and which to float. */ 280 j = n_uncol - 1; 281 i = n_col - 1; 282 for(fp=&a->frame; *fp;) { 283 f = *fp; 284 if(f != a->sel) { 285 if(!f->collapsed) { 286 if(j < 0) 287 f->collapsed = true; 288 j--; 289 } 290 if(f->collapsed) { 291 if(i < 0) { 292 f->collapsed = false; 293 area_moveto(f->view->floating, f); 294 continue; 295 } 296 i--; 297 } 298 } 299 /* Doesn't change if we 'continue' */ 300 fp = &f->anext; 301 } 302 303 if(n_colp) *n_colp = n_col; 304 if(n_uncolp) *n_uncolp = n_uncol; 305 } 306 307 void 308 column_settle(Area *a) { 309 Frame *f; 310 uint yoff, yoffcr; 311 int surplus, n_uncol, n; 312 313 n_uncol = 0; 314 surplus = column_surplus(a); 315 for(f=a->frame; f; f=f->anext) 316 if(!f->collapsed) n_uncol++; 317 318 if(n_uncol == 0) { 319 fprint(2, "%s: Badness: No uncollapsed frames, column %d, view %q\n", 320 argv0, area_idx(a), a->view->name); 321 return; 322 } 323 if(surplus < 0) 324 fprint(2, "%s: Badness: surplus = %d in column_settle, column %d, view %q\n", 325 argv0, surplus, area_idx(a), a->view->name); 326 327 yoff = a->r.min.y; 328 yoffcr = yoff; 329 n = surplus % n_uncol; 330 surplus /= n_uncol; 331 for(f=a->frame; f; f=f->anext) { 332 f->r = rectsetorigin(f->r, Pt(a->r.min.x, yoff)); 333 f->colr = rectsetorigin(f->colr, Pt(a->r.min.x, yoffcr)); 334 f->r.min.x = a->r.min.x; 335 f->r.max.x = a->r.max.x; 336 if(def.incmode == ISqueeze && !resizing) 337 if(!f->collapsed) { 338 f->r.max.y += surplus; 339 if(n-- > 0) 340 f->r.max.y++; 341 } 342 yoff = f->r.max.y; 343 yoffcr = f->colr.max.y; 344 } 345 } 346 347 /* 348 * Returns how much a frame "wants" to grow. 349 */ 350 static int 351 foo(Frame *f) { 352 WinHints h; 353 int maxh; 354 355 h = frame_gethints(f); 356 maxh = 0; 357 if(h.aspect.max.x) 358 maxh = h.baspect.y + 359 (Dx(f->r) - h.baspect.x) * 360 h.aspect.max.y / h.aspect.max.x; 361 maxh = max(maxh, h.max.y); 362 363 if(Dy(f->r) >= maxh) 364 return 0; 365 return h.inc.y - (Dy(f->r) - h.base.y) % h.inc.y; 366 } 367 368 static int 369 comp_frame(const void *a, const void *b) { 370 int ia, ib; 371 372 ia = foo(*(Frame**)a); 373 ib = foo(*(Frame**)b); 374 /* 375 * I'd like to favor the selected client, but 376 * it causes windows to jump as focus changes. 377 */ 378 return ia < ib ? -1 : 379 ia > ib ? 1 : 380 0; 381 } 382 383 static void 384 column_squeeze(Area *a) { 385 static Vector_ptr fvec; 386 Frame *f; 387 int surplus, osurplus, dy, i; 388 389 fvec.n = 0; 390 for(f=a->frame; f; f=f->anext) 391 if(!f->collapsed) { 392 f->r = frame_hints(f, f->r, 0); 393 vector_ppush(&fvec, f); 394 } 395 396 surplus = column_surplus(a); 397 for(osurplus=0; surplus != osurplus;) { 398 osurplus = surplus; 399 qsort(fvec.ary, fvec.n, sizeof *fvec.ary, comp_frame); 400 for(i=0; i < fvec.n; i++) { 401 f=fvec.ary[i]; 402 dy = foo(f); 403 if(dy > surplus) 404 break; 405 surplus -= dy; 406 f->r.max.y += dy; 407 } 408 } 409 } 410 411 /* 412 * Frobs a column. Which is to say, *temporary* kludge. 413 * Essentially seddles the column and resizes its clients. 414 */ 415 void 416 column_frob(Area *a) { 417 Frame *f; 418 419 for(f=a->frame; f; f=f->anext) 420 f->r = f->colr; 421 column_settle(a); 422 if(a->view == selview) 423 for(f=a->frame; f; f=f->anext) 424 client_resize(f->client, f->r); 425 } 426 427 static void 428 column_scale(Area *a) { 429 Frame *f; 430 uint dy; 431 uint ncol, nuncol; 432 uint colh; 433 int surplus; 434 435 if(!a->frame) 436 return; 437 438 column_fit(a, &ncol, &nuncol); 439 440 colh = labelh(def.font); 441 if(a->max && !resizing) 442 colh = 0; 443 444 dy = 0; 445 surplus = Dy(a->r); 446 for(f=a->frame; f; f=f->anext) { 447 if(f->collapsed) 448 f->colr.max.y = f->colr.min.y + colh; 449 else if(Dy(f->colr) == 0) 450 f->colr.max.y++; 451 surplus -= Dy(f->colr); 452 if(!f->collapsed) 453 dy += Dy(f->colr); 454 } 455 for(f=a->frame; f; f=f->anext) { 456 f->dy = Dy(f->colr); 457 f->colr.min.x = a->r.min.x; 458 f->colr.max.x = a->r.max.x; 459 if(!f->collapsed) 460 f->colr.max.y += ((float)f->dy / dy) * surplus; 461 if(btassert("6 full", !(f->collapsed ? Dy(f->r) >= 0 : dy > 0))) 462 warning("Something's fucked: %s:%d:%s()", 463 __FILE__, __LINE__, __func__); 464 frame_resize(f, f->colr); 465 } 466 467 if(def.incmode == ISqueeze && !resizing) 468 column_squeeze(a); 469 column_settle(a); 470 } 471 472 void 473 column_arrange(Area *a, bool dirty) { 474 Frame *f; 475 View *v; 476 477 if(a->floating) 478 float_arrange(a); 479 if(a->floating || !a->frame) 480 return; 481 482 v = a->view; 483 484 switch(a->mode) { 485 case Coldefault: 486 if(dirty) 487 for(f=a->frame; f; f=f->anext) 488 f->colr = Rect(0, 0, 100, 100); 489 break; 490 case Colstack: 491 /* XXX */ 492 for(f=a->frame; f; f=f->anext) 493 f->collapsed = (f != a->sel); 494 break; 495 default: 496 fprint(2, "Dieing: %s: screen: %d a: %p mode: %x floating: %d\n", 497 v->name, a->screen, a, a->mode, a->floating); 498 die("not reached"); 499 break; 500 } 501 column_scale(a); 502 /* XXX */ 503 if(a->sel->collapsed) 504 area_setsel(a, a->sel); 505 if(v == selview) { 506 //view_restack(v); 507 client_resize(a->sel->client, a->sel->r); 508 for(f=a->frame; f; f=f->anext) 509 client_resize(f->client, f->r); 510 } 511 } 512 513 void 514 column_resize(Area *a, int w) { 515 Area *an; 516 int dw; 517 518 an = a->next; 519 assert(an != nil); 520 521 dw = w - Dx(a->r); 522 a->r.max.x += dw; 523 an->r.min.x += dw; 524 525 /* view_arrange(a->view); */ 526 view_update(a->view); 527 } 528 529 static void 530 column_resizeframe_h(Frame *f, Rectangle r) { 531 Area *a; 532 Frame *fn, *fp; 533 uint minh; 534 535 minh = labelh(def.font); 536 537 a = f->area; 538 fn = f->anext; 539 fp = f->aprev; 540 541 if(fp) 542 r.min.y = max(r.min.y, fp->colr.min.y + minh); 543 else 544 r.min.y = max(r.min.y, a->r.min.y); 545 546 if(fn) 547 r.max.y = min(r.max.y, fn->colr.max.y - minh); 548 else 549 r.max.y = min(r.max.y, a->r.max.y); 550 551 if(fp) { 552 fp->colr.max.y = r.min.y; 553 frame_resize(fp, fp->colr); 554 } 555 else 556 r.min.y = min(r.min.y, r.max.y - minh); 557 558 if(fn) { 559 fn->colr.min.y = r.max.y; 560 frame_resize(fn, fn->colr); 561 } 562 else 563 r.max.y = max(r.max.y, r.min.y + minh); 564 565 f->colr = r; 566 frame_resize(f, r); 567 } 568 569 void 570 column_resizeframe(Frame *f, Rectangle r) { 571 Area *a, *al, *ar; 572 View *v; 573 uint minw; 574 575 a = f->area; 576 v = a->view; 577 578 minw = column_minwidth(); 579 580 al = a->prev; 581 ar = a->next; 582 583 if(al) 584 r.min.x = max(r.min.x, al->r.min.x + minw); 585 else { /* Hm... */ 586 r.min.x = max(r.min.x, v->r[a->screen].min.x); 587 r.max.x = max(r.max.x, r.min.x + minw); 588 } 589 590 if(ar) 591 r.max.x = min(r.max.x, ar->r.max.x - minw); 592 else { 593 r.max.x = min(r.max.x, v->r[a->screen].max.x); 594 r.min.x = min(r.min.x, r.max.x - minw); 595 } 596 597 a->r.min.x = r.min.x; 598 a->r.max.x = r.max.x; 599 if(al) { 600 al->r.max.x = a->r.min.x; 601 column_arrange(al, false); 602 }else { 603 v->pad[a->screen].min.x = r.min.x - v->r[a->screen].min.x; 604 } 605 if(ar) { 606 ar->r.min.x = a->r.max.x; 607 column_arrange(ar, false); 608 }else { 609 v->pad[a->screen].max.x = r.max.x - v->r[a->screen].max.x; 610 } 611 612 column_resizeframe_h(f, r); 613 614 view_update(v); 615 } 616