wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

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