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
column_setmode(Area * a,const char * mode)19 column_setmode(Area *a, const char *mode) {
20 char *str, *tok, *orig;
21 char 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 orig = strdup(mode);
31 str = orig;
32 old = '\0';
33 while(*(tok = str)) {
34 add = old;
35 while((old=*str) && !strchr("+-^", old))
36 str++;
37 *str = '\0';
38 if(str > tok) {
39 print("'%s' %c\n", tok, add);
40 if(!strcmp(tok, "max")) {
41 if(add == '\0' || add == '+')
42 a->max = true;
43 else if(add == '-')
44 a->max = false;
45 else
46 a->max = !a->max;
47 }else
48 if(!strcmp(tok, "stack")) {
49 if(add == '\0' || add == '+')
50 a->mode = Colstack;
51 else if(add == '-')
52 a->mode = Coldefault;
53 else
54 a->mode = a->mode == Colstack ? Coldefault : Colstack;
55 }else
56 if(!strcmp(tok, "default")) {
57 if(add == '\0' || add == '+') {
58 a->mode = Coldefault;
59 column_arrange(a, true);
60 }else if(add == '-')
61 a->mode = Colstack;
62 else
63 a->mode = a->mode == Coldefault ? Colstack : Coldefault;
64 }else {
65 free(orig);
66 return false;
67 }
68 }
69 if(old)
70 str++;
71
72 }
73 free(orig);
74 return true;
75 }
76
77 char*
column_getmode(Area * a)78 column_getmode(Area *a) {
79 return sxprint("%s%cmax", a->mode == Colstack ? "stack" : "default",
80 a->max ? '+' : '-');
81 }
82
83 int
column_minwidth(void)84 column_minwidth(void)
85 {
86 return 4 * labelh(def.font);
87 }
88
89 Area*
column_new(View * v,Area * pos,int scrn,uint w)90 column_new(View *v, Area *pos, int scrn, uint w) {
91 Area *a;
92
93 assert(!pos || !pos->floating && pos->screen == scrn);
94 a = area_create(v, pos, scrn, w);
95 return a;
96 #if 0
97 if(!a)
98 return nil;
99
100 view_arrange(v);
101 view_update(v);
102 #endif
103 }
104
105 void
column_insert(Area * a,Frame * f,Frame * pos)106 column_insert(Area *a, Frame *f, Frame *pos) {
107
108 f->area = a;
109 f->client->floating = false;
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 /* Temporary. */
118 static void
stack_scale(Frame * first,int height)119 stack_scale(Frame *first, int height) {
120 Frame *f;
121 Area *a;
122 uint dy;
123 int surplus;
124
125 a = first->area;
126
127 /*
128 * Will need something like this.
129 column_fit(a, &ncol, &nuncol);
130 */
131
132 dy = 0;
133 for(f=first; f && !f->collapsed; f=f->anext)
134 dy += Dy(f->colr);
135
136 /* Distribute the surplus.
137 */
138 surplus = height - dy;
139 for(f=first; f && !f->collapsed; f=f->anext)
140 f->colr.max.y += ((float)Dy(f->r) / dy) * surplus;
141 }
142
143 static void
stack_info(Frame * f,Frame ** firstp,Frame ** lastp,int * dyp,int * nframep)144 stack_info(Frame *f, Frame **firstp, Frame **lastp, int *dyp, int *nframep) {
145 Frame *ft, *first, *last;
146 int dy, nframe;
147
148 nframe = 0;
149 dy = 0;
150 first = f;
151 last = f;
152
153 for(ft=f; ft && ft->collapsed; ft=ft->anext)
154 ;
155 if(ft && ft != f) {
156 f = ft;
157 dy += Dy(f->colr);
158 }
159 for(ft=f; ft && !ft->collapsed; ft=ft->aprev) {
160 first = ft;
161 nframe++;
162 dy += Dy(ft->colr);
163 }
164 for(ft=f->anext; ft && !ft->collapsed; ft=ft->anext) {
165 if(first == nil)
166 first = ft;
167 last = ft;
168 nframe++;
169 dy += Dy(ft->colr);
170 }
171 if(nframep) *nframep = nframe;
172 if(firstp) *firstp = first;
173 if(lastp) *lastp = last;
174 if(dyp) *dyp = dy;
175 }
176
177 int
stack_count(Frame * f,int * mp)178 stack_count(Frame *f, int *mp) {
179 Frame *fp;
180 int n, m;
181
182 n = 0;
183 for(fp=f->aprev; fp && fp->collapsed; fp=fp->aprev)
184 n++;
185 m = ++n;
186 for(fp=f->anext; fp && fp->collapsed; fp=fp->anext)
187 n++;
188 if(mp) *mp = m;
189 return n;
190 }
191
192 Frame*
stack_find(Area * a,Frame * f,int dir,bool stack)193 stack_find(Area *a, Frame *f, int dir, bool stack) {
194 Frame *fp;
195
196 switch (dir) {
197 default:
198 die("not reached");
199 case North:
200 if(f)
201 for(f=f->aprev; f && f->collapsed && stack; f=f->aprev)
202 ;
203 else {
204 f = nil;
205 for(fp=a->frame; fp; fp=fp->anext)
206 if(!fp->collapsed || !stack)
207 f = fp;
208 }
209 break;
210 case South:
211 if(f)
212 for(f=f->anext; f && f->collapsed && stack; f=f->anext)
213 ;
214 else
215 for(f=a->frame; f && f->collapsed && stack; f=f->anext)
216 ;
217 break;
218 }
219 return f;
220 }
221
222 /* TODO: Move elsewhere. */
223 bool
find(Area ** ap,Frame ** fp,int dir,bool wrap,bool stack)224 find(Area **ap, Frame **fp, int dir, bool wrap, bool stack) {
225 Rectangle r;
226 Frame *f;
227 Area *a;
228
229 f = *fp;
230 a = *ap;
231 r = f ? f->r : a->r;
232
233 if(dir == North || dir == South) {
234 *fp = stack_find(a, f, dir, stack);
235 if(*fp)
236 return true;
237 if (!a->floating)
238 *ap = area_find(a->view, r, dir, wrap);
239 if(!*ap)
240 return false;
241 *fp = stack_find(*ap, *fp, dir, stack);
242 return true;
243 }
244 if(dir != East && dir != West)
245 die("not reached");
246 *ap = area_find(a->view, r, dir, wrap);
247 if(!*ap)
248 return false;
249 *fp = ap[0]->sel;
250 return true;
251 }
252
253 void
column_attach(Area * a,Frame * f)254 column_attach(Area *a, Frame *f) {
255 Frame *first;
256 int nframe, dy, h;
257
258 f->colr = a->r;
259
260 if(a->sel) {
261 stack_info(a->sel, &first, nil, &dy, &nframe);
262 h = dy / (nframe+1);
263 f->colr.max.y = f->colr.min.y + h;
264 stack_scale(first, dy - h);
265 }
266
267 column_insert(a, f, a->sel);
268 column_arrange(a, false);
269 }
270
271 void
column_detach(Frame * f)272 column_detach(Frame *f) {
273 Frame *first;
274 Area *a;
275 int dy;
276
277 a = f->area;
278 stack_info(f, &first, nil, &dy, nil);
279 if(first && first == f)
280 first = f->anext;
281 column_remove(f);
282 if(a->frame) {
283 if(first)
284 stack_scale(first, dy);
285 column_arrange(a, false);
286 }else if(a->view->areas[a->screen]->next)
287 area_destroy(a);
288 }
289
290 static void column_scale(Area*);
291
292 void
column_attachrect(Area * a,Frame * f,Rectangle r)293 column_attachrect(Area *a, Frame *f, Rectangle r) {
294 Frame *fp, *pos;
295 int before, after;
296
297 pos = nil;
298 for(fp=a->frame; fp; pos=fp, fp=fp->anext) {
299 if(r.max.y < fp->r.min.y || r.min.y > fp->r.max.y)
300 continue;
301 before = fp->r.min.y - r.min.y;
302 after = -fp->r.max.y + r.max.y;
303 }
304 column_insert(a, f, pos);
305 column_resizeframe_h(f, r);
306 column_scale(a);
307 }
308
309 void
column_remove(Frame * f)310 column_remove(Frame *f) {
311 Frame *pr;
312 Area *a;
313
314 a = f->area;
315 pr = f->aprev;
316
317 frame_remove(f);
318
319 f->area = nil;
320 if(a->sel == f) {
321 if(pr == nil)
322 pr = a->frame;
323 if(pr && pr->collapsed)
324 if(pr->anext && !pr->anext->collapsed)
325 pr = pr->anext;
326 else
327 pr->collapsed = false;
328 a->sel = nil;
329 area_setsel(a, pr);
330 }
331 }
332
333 static int
column_surplus(Area * a)334 column_surplus(Area *a) {
335 Frame *f;
336 int surplus;
337
338 surplus = Dy(a->r);
339 for(f=a->frame; f; f=f->anext)
340 surplus -= Dy(f->r);
341 return surplus;
342 }
343
344 static void
column_fit(Area * a,uint * n_colp,uint * n_uncolp)345 column_fit(Area *a, uint *n_colp, uint *n_uncolp) {
346 Frame *f, **fp;
347 uint minh, dy;
348 uint n_col, n_uncol;
349 uint col_h, uncol_h;
350 int surplus, i, j;
351
352 /* The minimum heights of collapsed and uncollpsed frames.
353 */
354 minh = labelh(def.font);
355 col_h = labelh(def.font);
356 uncol_h = minh + col_h + 1;
357 if(a->max && !resizing)
358 col_h = 0;
359
360 /* Count collapsed and uncollapsed frames. */
361 n_col = 0;
362 n_uncol = 0;
363 for(f=a->frame; f; f=f->anext) {
364 frame_resize(f, f->colr);
365 if(f->collapsed)
366 n_col++;
367 else
368 n_uncol++;
369 }
370
371 if(n_uncol == 0) {
372 n_uncol++;
373 n_col--;
374 (a->sel ? a->sel : a->frame)->collapsed = false;
375 }
376
377 /* FIXME: Kludge. See frame_attachrect. */
378 dy = Dy(a->view->r[a->screen]) - Dy(a->r);
379 minh = col_h * (n_col + n_uncol - 1) + uncol_h;
380 if(dy && Dy(a->r) < minh)
381 a->r.max.y += min(dy, minh - Dy(a->r));
382
383 surplus = Dy(a->r)
384 - (n_col * col_h)
385 - (n_uncol * uncol_h);
386
387 /* Collapse until there is room */
388 if(surplus < 0) {
389 i = ceil(-1.F * surplus / (uncol_h - col_h));
390 if(i >= n_uncol)
391 i = n_uncol - 1;
392 n_uncol -= i;
393 n_col += i;
394 surplus += i * (uncol_h - col_h);
395 }
396 /* Push to the floating layer until there is room */
397 if(surplus < 0) {
398 i = ceil(-1.F * surplus / col_h);
399 if(i > n_col)
400 i = n_col;
401 n_col -= i;
402 surplus += i * col_h;
403 }
404
405 /* Decide which to collapse and which to float. */
406 j = n_uncol - 1;
407 i = n_col - 1;
408 for(fp=&a->frame; *fp;) {
409 f = *fp;
410 if(f != a->sel) {
411 if(!f->collapsed) {
412 if(j < 0)
413 f->collapsed = true;
414 j--;
415 }
416 if(f->collapsed) {
417 if(i < 0) {
418 f->collapsed = false;
419 area_moveto(f->view->floating, f);
420 continue;
421 }
422 i--;
423 }
424 }
425 /* Doesn't change if we 'continue' */
426 fp = &f->anext;
427 }
428
429 if(n_colp) *n_colp = n_col;
430 if(n_uncolp) *n_uncolp = n_uncol;
431 }
432
433 void
column_settle(Area * a)434 column_settle(Area *a) {
435 Frame *f;
436 uint yoff, yoffcr;
437 int surplus, n_uncol, n;
438
439 n_uncol = 0;
440 surplus = column_surplus(a);
441 for(f=a->frame; f; f=f->anext)
442 if(!f->collapsed) n_uncol++;
443
444 if(n_uncol == 0) {
445 fprint(2, "%s: Badness: No uncollapsed frames, column %d, view %q\n",
446 argv0, area_idx(a), a->view->name);
447 return;
448 }
449 if(surplus < 0)
450 fprint(2, "%s: Badness: surplus = %d in column_settle, column %d, view %q\n",
451 argv0, surplus, area_idx(a), a->view->name);
452
453 yoff = a->r.min.y;
454 yoffcr = yoff;
455 n = surplus % n_uncol;
456 surplus /= n_uncol;
457 for(f=a->frame; f; f=f->anext) {
458 f->r = rectsetorigin(f->r, Pt(a->r.min.x, yoff));
459 f->colr = rectsetorigin(f->colr, Pt(a->r.min.x, yoffcr));
460 f->r.min.x = a->r.min.x;
461 f->r.max.x = a->r.max.x;
462 if(def.incmode == ISqueeze && !resizing)
463 if(!f->collapsed) {
464 f->r.max.y += surplus;
465 if(n-- > 0)
466 f->r.max.y++;
467 }
468 yoff = f->r.max.y;
469 yoffcr = f->colr.max.y;
470 }
471 }
472
473 /*
474 * Returns how much a frame "wants" to grow.
475 */
476 static int
foo(Frame * f)477 foo(Frame *f) {
478 WinHints h;
479 int maxh;
480
481 h = frame_gethints(f);
482 maxh = 0;
483 if(h.aspect.max.x)
484 maxh = h.baspect.y +
485 (Dx(f->r) - h.baspect.x) *
486 h.aspect.max.y / h.aspect.max.x;
487 maxh = max(maxh, h.max.y);
488
489 if(Dy(f->r) >= maxh)
490 return 0;
491 return h.inc.y - (Dy(f->r) - h.base.y) % h.inc.y;
492 }
493
494 static int
comp_frame(const void * a,const void * b)495 comp_frame(const void *a, const void *b) {
496 int ia, ib;
497
498 ia = foo(*(Frame**)a);
499 ib = foo(*(Frame**)b);
500 /*
501 * I'd like to favor the selected client, but
502 * it causes windows to jump as focus changes.
503 */
504 return ia < ib ? -1 :
505 ia > ib ? 1 :
506 0;
507 }
508
509 static void
column_squeeze(Area * a)510 column_squeeze(Area *a) {
511 static Vector_ptr fvec;
512 Frame *f;
513 int surplus, osurplus, dy, i;
514
515 fvec.n = 0;
516 for(f=a->frame; f; f=f->anext)
517 if(!f->collapsed) {
518 f->r = frame_hints(f, f->r, 0);
519 vector_ppush(&fvec, f);
520 }
521
522 surplus = column_surplus(a);
523 for(osurplus=0; surplus != osurplus;) {
524 osurplus = surplus;
525 qsort(fvec.ary, fvec.n, sizeof *fvec.ary, comp_frame);
526 for(i=0; i < fvec.n; i++) {
527 f=fvec.ary[i];
528 dy = foo(f);
529 if(dy > surplus)
530 break;
531 surplus -= dy;
532 f->r.max.y += dy;
533 }
534 }
535 }
536
537 /*
538 * Frobs a column. Which is to say, *temporary* kludge.
539 * Essentially seddles the column and resizes its clients.
540 */
541 void
column_frob(Area * a)542 column_frob(Area *a) {
543 Frame *f;
544
545 for(f=a->frame; f; f=f->anext)
546 f->r = f->colr;
547 column_settle(a);
548 if(a->view == selview)
549 for(f=a->frame; f; f=f->anext)
550 client_resize(f->client, f->r);
551 }
552
553 static void
column_scale(Area * a)554 column_scale(Area *a) {
555 Frame *f;
556 uint dy;
557 uint ncol, nuncol;
558 uint colh;
559 int surplus;
560
561 if(!a->frame)
562 return;
563
564 column_fit(a, &ncol, &nuncol);
565
566 colh = labelh(def.font);
567 if(a->max && !resizing)
568 colh = 0;
569
570 dy = 0;
571 surplus = Dy(a->r);
572 for(f=a->frame; f; f=f->anext) {
573 if(f->collapsed)
574 f->colr.max.y = f->colr.min.y + colh;
575 else if(Dy(f->colr) == 0)
576 f->colr.max.y++;
577 surplus -= Dy(f->colr);
578 if(!f->collapsed)
579 dy += Dy(f->colr);
580 }
581 for(f=a->frame; f; f=f->anext) {
582 f->dy = Dy(f->colr);
583 f->colr.min.x = a->r.min.x;
584 f->colr.max.x = a->r.max.x;
585 if(!f->collapsed)
586 f->colr.max.y += ((float)f->dy / dy) * surplus;
587 if(btassert("6 full", !(f->collapsed ? Dy(f->r) >= 0 : dy > 0)))
588 warning("Something's fucked: %s:%d:%s()",
589 __FILE__, __LINE__, __func__);
590 frame_resize(f, f->colr);
591 }
592
593 if(def.incmode == ISqueeze && !resizing)
594 column_squeeze(a);
595 column_settle(a);
596 }
597
598 void
column_arrange(Area * a,bool dirty)599 column_arrange(Area *a, bool dirty) {
600 Frame *f;
601 View *v;
602
603 if(a->floating)
604 float_arrange(a);
605 if(a->floating || !a->frame)
606 return;
607
608 v = a->view;
609
610 switch(a->mode) {
611 case Coldefault:
612 if(dirty)
613 for(f=a->frame; f; f=f->anext)
614 f->colr = Rect(0, 0, 100, 100);
615 break;
616 case Colstack:
617 /* XXX */
618 for(f=a->frame; f; f=f->anext)
619 f->collapsed = (f != a->sel);
620 break;
621 default:
622 fprint(2, "Dieing: %s: screen: %d a: %p mode: %x floating: %d\n",
623 v->name, a->screen, a, a->mode, a->floating);
624 die("not reached");
625 break;
626 }
627 column_scale(a);
628 /* XXX */
629 if(a->sel->collapsed)
630 area_setsel(a, a->sel);
631 if(v == selview) {
632 //view_restack(v);
633 client_resize(a->sel->client, a->sel->r);
634 for(f=a->frame; f; f=f->anext)
635 client_resize(f->client, f->r);
636 }
637 }
638
639 void
column_resize(Area * a,int w)640 column_resize(Area *a, int w) {
641 Area *an;
642 int dw;
643
644 an = a->next;
645 assert(an != nil);
646
647 dw = w - Dx(a->r);
648 a->r.max.x += dw;
649 an->r.min.x += dw;
650
651 /* view_arrange(a->view); */
652 view_update(a->view);
653 }
654
655 static void
column_resizeframe_h(Frame * f,Rectangle r)656 column_resizeframe_h(Frame *f, Rectangle r) {
657 Area *a;
658 Frame *fn, *fp;
659 uint minh;
660
661 minh = labelh(def.font);
662
663 a = f->area;
664 fn = f->anext;
665 fp = f->aprev;
666
667 if(fp)
668 r.min.y = max(r.min.y, fp->colr.min.y + minh);
669 else
670 r.min.y = max(r.min.y, a->r.min.y);
671
672 if(fn)
673 r.max.y = min(r.max.y, fn->colr.max.y - minh);
674 else
675 r.max.y = min(r.max.y, a->r.max.y);
676
677 if(fp) {
678 fp->colr.max.y = r.min.y;
679 frame_resize(fp, fp->colr);
680 }
681 else
682 r.min.y = min(r.min.y, r.max.y - minh);
683
684 if(fn) {
685 fn->colr.min.y = r.max.y;
686 frame_resize(fn, fn->colr);
687 }
688 else
689 r.max.y = max(r.max.y, r.min.y + minh);
690
691 f->colr = r;
692 frame_resize(f, r);
693 }
694
695 void
column_resizeframe(Frame * f,Rectangle r)696 column_resizeframe(Frame *f, Rectangle r) {
697 Area *a, *al, *ar;
698 View *v;
699 uint minw;
700
701 a = f->area;
702 v = a->view;
703
704 minw = column_minwidth();
705
706 al = a->prev;
707 ar = a->next;
708
709 if(al)
710 r.min.x = max(r.min.x, al->r.min.x + minw);
711 else { /* Hm... */
712 r.min.x = max(r.min.x, v->r[a->screen].min.x);
713 r.max.x = max(r.max.x, r.min.x + minw);
714 }
715
716 if(ar)
717 r.max.x = min(r.max.x, ar->r.max.x - minw);
718 else {
719 r.max.x = min(r.max.x, v->r[a->screen].max.x);
720 r.min.x = min(r.min.x, r.max.x - minw);
721 }
722
723 a->r.min.x = r.min.x;
724 a->r.max.x = r.max.x;
725 if(al) {
726 al->r.max.x = a->r.min.x;
727 column_arrange(al, false);
728 }
729 if(ar) {
730 ar->r.min.x = a->r.max.x;
731 column_arrange(ar, false);
732 }
733
734 column_resizeframe_h(f, r);
735
736 view_update(v);
737 }
738
739