/* Copyright ©2004-2006 Anselm R. Garbe * Copyright ©2006-2010 Kris Maglione * See LICENSE file for license details. */ #include "dat.h" #include #include #include "fns.h" static void column_resizeframe_h(Frame*, Rectangle); char *modes[] = { [Coldefault] = "default", [Colstack] = "stack", [Colmax] = "max", }; bool column_setmode(Area *a, const char *mode) { char *str, *tok, *orig; char add, old; /* * The mapping between the current internal * representation and the external interface * is currently a bit complex. That will probably * change. */ orig = strdup(mode); str = orig; old = '\0'; while(*(tok = str)) { add = old; while((old=*str) && !strchr("+-^", old)) str++; *str = '\0'; if(str > tok) { print("'%s' %c\n", tok, add); if(!strcmp(tok, "max")) { if(add == '\0' || add == '+') a->max = true; else if(add == '-') a->max = false; else a->max = !a->max; }else if(!strcmp(tok, "stack")) { if(add == '\0' || add == '+') a->mode = Colstack; else if(add == '-') a->mode = Coldefault; else a->mode = a->mode == Colstack ? Coldefault : Colstack; }else if(!strcmp(tok, "default")) { if(add == '\0' || add == '+') { a->mode = Coldefault; column_arrange(a, true); }else if(add == '-') a->mode = Colstack; else a->mode = a->mode == Coldefault ? Colstack : Coldefault; }else { free(orig); return false; } } if(old) str++; } free(orig); return true; } char* column_getmode(Area *a) { return sxprint("%s%cmax", a->mode == Colstack ? "stack" : "default", a->max ? '+' : '-'); } int column_minwidth(void) { return 4 * labelh(def.font); } Area* column_new(View *v, Area *pos, int scrn, uint w) { Area *a; assert(!pos || !pos->floating && pos->screen == scrn); a = area_create(v, pos, scrn, w); return a; #if 0 if(!a) return nil; view_arrange(v); view_update(v); #endif } void column_insert(Area *a, Frame *f, Frame *pos) { f->area = a; f->client->floating = false; f->screen = a->screen; f->column = area_idx(a); frame_insert(f, pos); if(a->sel == nil) area_setsel(a, f); } /* Temporary. */ static void stack_scale(Frame *first, int height) { Frame *f; Area *a; uint dy; int surplus; a = first->area; /* * Will need something like this. column_fit(a, &ncol, &nuncol); */ dy = 0; for(f=first; f && !f->collapsed; f=f->anext) dy += Dy(f->colr); /* Distribute the surplus. */ surplus = height - dy; for(f=first; f && !f->collapsed; f=f->anext) f->colr.max.y += ((float)Dy(f->r) / dy) * surplus; } static void stack_info(Frame *f, Frame **firstp, Frame **lastp, int *dyp, int *nframep) { Frame *ft, *first, *last; int dy, nframe; nframe = 0; dy = 0; first = f; last = f; for(ft=f; ft && ft->collapsed; ft=ft->anext) ; if(ft && ft != f) { f = ft; dy += Dy(f->colr); } for(ft=f; ft && !ft->collapsed; ft=ft->aprev) { first = ft; nframe++; dy += Dy(ft->colr); } for(ft=f->anext; ft && !ft->collapsed; ft=ft->anext) { if(first == nil) first = ft; last = ft; nframe++; dy += Dy(ft->colr); } if(nframep) *nframep = nframe; if(firstp) *firstp = first; if(lastp) *lastp = last; if(dyp) *dyp = dy; } int stack_count(Frame *f, int *mp) { Frame *fp; int n, m; n = 0; for(fp=f->aprev; fp && fp->collapsed; fp=fp->aprev) n++; m = ++n; for(fp=f->anext; fp && fp->collapsed; fp=fp->anext) n++; if(mp) *mp = m; return n; } Frame* stack_find(Area *a, Frame *f, int dir, bool stack) { Frame *fp; switch (dir) { default: die("not reached"); case North: if(f) for(f=f->aprev; f && f->collapsed && stack; f=f->aprev) ; else { f = nil; for(fp=a->frame; fp; fp=fp->anext) if(!fp->collapsed || !stack) f = fp; } break; case South: if(f) for(f=f->anext; f && f->collapsed && stack; f=f->anext) ; else for(f=a->frame; f && f->collapsed && stack; f=f->anext) ; break; } return f; } /* TODO: Move elsewhere. */ bool find(Area **ap, Frame **fp, int dir, bool wrap, bool stack) { Rectangle r; Frame *f; Area *a; f = *fp; a = *ap; r = f ? f->r : a->r; if(dir == North || dir == South) { *fp = stack_find(a, f, dir, stack); if(*fp) return true; if (!a->floating) *ap = area_find(a->view, r, dir, wrap); if(!*ap) return false; *fp = stack_find(*ap, *fp, dir, stack); return true; } if(dir != East && dir != West) die("not reached"); *ap = area_find(a->view, r, dir, wrap); if(!*ap) return false; *fp = ap[0]->sel; return true; } void column_attach(Area *a, Frame *f) { Frame *first; int nframe, dy, h; f->colr = a->r; if(a->sel) { stack_info(a->sel, &first, nil, &dy, &nframe); h = dy / (nframe+1); f->colr.max.y = f->colr.min.y + h; stack_scale(first, dy - h); } column_insert(a, f, a->sel); column_arrange(a, false); } void column_detach(Frame *f) { Frame *first; Area *a; int dy; a = f->area; stack_info(f, &first, nil, &dy, nil); if(first && first == f) first = f->anext; column_remove(f); if(a->frame) { if(first) stack_scale(first, dy); column_arrange(a, false); }else if(a->view->areas[a->screen]->next) area_destroy(a); } static void column_scale(Area*); void column_attachrect(Area *a, Frame *f, Rectangle r) { Frame *fp, *pos; int before, after; pos = nil; for(fp=a->frame; fp; pos=fp, fp=fp->anext) { if(r.max.y < fp->r.min.y || r.min.y > fp->r.max.y) continue; before = fp->r.min.y - r.min.y; after = -fp->r.max.y + r.max.y; } column_insert(a, f, pos); column_resizeframe_h(f, r); column_scale(a); } void column_remove(Frame *f) { Frame *pr; Area *a; a = f->area; pr = f->aprev; frame_remove(f); f->area = nil; if(a->sel == f) { if(pr == nil) pr = a->frame; if(pr && pr->collapsed) if(pr->anext && !pr->anext->collapsed) pr = pr->anext; else pr->collapsed = false; a->sel = nil; area_setsel(a, pr); } } static int column_surplus(Area *a) { Frame *f; int surplus; surplus = Dy(a->r); for(f=a->frame; f; f=f->anext) surplus -= Dy(f->r); return surplus; } static void column_fit(Area *a, uint *n_colp, uint *n_uncolp) { Frame *f, **fp; uint minh, dy; uint n_col, n_uncol; uint col_h, uncol_h; int surplus, i, j; /* The minimum heights of collapsed and uncollpsed frames. */ minh = labelh(def.font); col_h = labelh(def.font); uncol_h = minh + col_h + 1; if(a->max && !resizing) col_h = 0; /* Count collapsed and uncollapsed frames. */ n_col = 0; n_uncol = 0; for(f=a->frame; f; f=f->anext) { frame_resize(f, f->colr); if(f->collapsed) n_col++; else n_uncol++; } if(n_uncol == 0) { n_uncol++; n_col--; (a->sel ? a->sel : a->frame)->collapsed = false; } /* FIXME: Kludge. See frame_attachrect. */ dy = Dy(a->view->r[a->screen]) - Dy(a->r); minh = col_h * (n_col + n_uncol - 1) + uncol_h; if(dy && Dy(a->r) < minh) a->r.max.y += min(dy, minh - Dy(a->r)); surplus = Dy(a->r) - (n_col * col_h) - (n_uncol * uncol_h); /* Collapse until there is room */ if(surplus < 0) { i = ceil(-1.F * surplus / (uncol_h - col_h)); if(i >= n_uncol) i = n_uncol - 1; n_uncol -= i; n_col += i; surplus += i * (uncol_h - col_h); } /* Push to the floating layer until there is room */ if(surplus < 0) { i = ceil(-1.F * surplus / col_h); if(i > n_col) i = n_col; n_col -= i; surplus += i * col_h; } /* Decide which to collapse and which to float. */ j = n_uncol - 1; i = n_col - 1; for(fp=&a->frame; *fp;) { f = *fp; if(f != a->sel) { if(!f->collapsed) { if(j < 0) f->collapsed = true; j--; } if(f->collapsed) { if(i < 0) { f->collapsed = false; area_moveto(f->view->floating, f); continue; } i--; } } /* Doesn't change if we 'continue' */ fp = &f->anext; } if(n_colp) *n_colp = n_col; if(n_uncolp) *n_uncolp = n_uncol; } void column_settle(Area *a) { Frame *f; uint yoff, yoffcr; int surplus, n_uncol, n; n_uncol = 0; surplus = column_surplus(a); for(f=a->frame; f; f=f->anext) if(!f->collapsed) n_uncol++; if(n_uncol == 0) { fprint(2, "%s: Badness: No uncollapsed frames, column %d, view %q\n", argv0, area_idx(a), a->view->name); return; } if(surplus < 0) fprint(2, "%s: Badness: surplus = %d in column_settle, column %d, view %q\n", argv0, surplus, area_idx(a), a->view->name); yoff = a->r.min.y; yoffcr = yoff; n = surplus % n_uncol; surplus /= n_uncol; for(f=a->frame; f; f=f->anext) { f->r = rectsetorigin(f->r, Pt(a->r.min.x, yoff)); f->colr = rectsetorigin(f->colr, Pt(a->r.min.x, yoffcr)); f->r.min.x = a->r.min.x; f->r.max.x = a->r.max.x; if(def.incmode == ISqueeze && !resizing) if(!f->collapsed) { f->r.max.y += surplus; if(n-- > 0) f->r.max.y++; } yoff = f->r.max.y; yoffcr = f->colr.max.y; } } /* * Returns how much a frame "wants" to grow. */ static int foo(Frame *f) { WinHints h; int maxh; h = frame_gethints(f); maxh = 0; if(h.aspect.max.x) maxh = h.baspect.y + (Dx(f->r) - h.baspect.x) * h.aspect.max.y / h.aspect.max.x; maxh = max(maxh, h.max.y); if(Dy(f->r) >= maxh) return 0; return h.inc.y - (Dy(f->r) - h.base.y) % h.inc.y; } static int comp_frame(const void *a, const void *b) { int ia, ib; ia = foo(*(Frame**)a); ib = foo(*(Frame**)b); /* * I'd like to favor the selected client, but * it causes windows to jump as focus changes. */ return ia < ib ? -1 : ia > ib ? 1 : 0; } static void column_squeeze(Area *a) { static Vector_ptr fvec; Frame *f; int surplus, osurplus, dy, i; fvec.n = 0; for(f=a->frame; f; f=f->anext) if(!f->collapsed) { f->r = frame_hints(f, f->r, 0); vector_ppush(&fvec, f); } surplus = column_surplus(a); for(osurplus=0; surplus != osurplus;) { osurplus = surplus; qsort(fvec.ary, fvec.n, sizeof *fvec.ary, comp_frame); for(i=0; i < fvec.n; i++) { f=fvec.ary[i]; dy = foo(f); if(dy > surplus) break; surplus -= dy; f->r.max.y += dy; } } } /* * Frobs a column. Which is to say, *temporary* kludge. * Essentially seddles the column and resizes its clients. */ void column_frob(Area *a) { Frame *f; for(f=a->frame; f; f=f->anext) f->r = f->colr; column_settle(a); if(a->view == selview) for(f=a->frame; f; f=f->anext) client_resize(f->client, f->r); } static void column_scale(Area *a) { Frame *f; uint dy; uint ncol, nuncol; uint colh; int surplus; if(!a->frame) return; column_fit(a, &ncol, &nuncol); colh = labelh(def.font); if(a->max && !resizing) colh = 0; dy = 0; surplus = Dy(a->r); for(f=a->frame; f; f=f->anext) { if(f->collapsed) f->colr.max.y = f->colr.min.y + colh; else if(Dy(f->colr) == 0) f->colr.max.y++; surplus -= Dy(f->colr); if(!f->collapsed) dy += Dy(f->colr); } for(f=a->frame; f; f=f->anext) { f->dy = Dy(f->colr); f->colr.min.x = a->r.min.x; f->colr.max.x = a->r.max.x; if(!f->collapsed) f->colr.max.y += ((float)f->dy / dy) * surplus; if(btassert("6 full", !(f->collapsed ? Dy(f->r) >= 0 : dy > 0))) warning("Something's fucked: %s:%d:%s()", __FILE__, __LINE__, __func__); frame_resize(f, f->colr); } if(def.incmode == ISqueeze && !resizing) column_squeeze(a); column_settle(a); } void column_arrange(Area *a, bool dirty) { Frame *f; View *v; if(a->floating) float_arrange(a); if(a->floating || !a->frame) return; v = a->view; switch(a->mode) { case Coldefault: if(dirty) for(f=a->frame; f; f=f->anext) f->colr = Rect(0, 0, 100, 100); break; case Colstack: /* XXX */ for(f=a->frame; f; f=f->anext) f->collapsed = (f != a->sel); break; default: fprint(2, "Dieing: %s: screen: %d a: %p mode: %x floating: %d\n", v->name, a->screen, a, a->mode, a->floating); die("not reached"); break; } column_scale(a); /* XXX */ if(a->sel->collapsed) area_setsel(a, a->sel); if(v == selview) { //view_restack(v); client_resize(a->sel->client, a->sel->r); for(f=a->frame; f; f=f->anext) client_resize(f->client, f->r); } } void column_resize(Area *a, int w) { Area *an; int dw; an = a->next; assert(an != nil); dw = w - Dx(a->r); a->r.max.x += dw; an->r.min.x += dw; /* view_arrange(a->view); */ view_update(a->view); } static void column_resizeframe_h(Frame *f, Rectangle r) { Area *a; Frame *fn, *fp; uint minh; minh = labelh(def.font); a = f->area; fn = f->anext; fp = f->aprev; if(fp) r.min.y = max(r.min.y, fp->colr.min.y + minh); else r.min.y = max(r.min.y, a->r.min.y); if(fn) r.max.y = min(r.max.y, fn->colr.max.y - minh); else r.max.y = min(r.max.y, a->r.max.y); if(fp) { fp->colr.max.y = r.min.y; frame_resize(fp, fp->colr); } else r.min.y = min(r.min.y, r.max.y - minh); if(fn) { fn->colr.min.y = r.max.y; frame_resize(fn, fn->colr); } else r.max.y = max(r.max.y, r.min.y + minh); f->colr = r; frame_resize(f, r); } void column_resizeframe(Frame *f, Rectangle r) { Area *a, *al, *ar; View *v; uint minw; a = f->area; v = a->view; minw = column_minwidth(); al = a->prev; ar = a->next; if(al) r.min.x = max(r.min.x, al->r.min.x + minw); else { /* Hm... */ r.min.x = max(r.min.x, v->r[a->screen].min.x); r.max.x = max(r.max.x, r.min.x + minw); } if(ar) r.max.x = min(r.max.x, ar->r.max.x - minw); else { r.max.x = min(r.max.x, v->r[a->screen].max.x); r.min.x = min(r.min.x, r.max.x - minw); } a->r.min.x = r.min.x; a->r.max.x = r.max.x; if(al) { al->r.max.x = a->r.min.x; column_arrange(al, false); } if(ar) { ar->r.min.x = a->r.max.x; column_arrange(ar, false); } column_resizeframe_h(f, r); view_update(v); }