15494e770Schristos /* $OpenBSD$ */
2698d5317Sjmmv
3698d5317Sjmmv /*
4ed4e6cd4Schristos * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
54e179ddaSchristos * Copyright (c) 2016 Stephen Kent <smkent@smkent.net>
6698d5317Sjmmv *
7698d5317Sjmmv * Permission to use, copy, modify, and distribute this software for any
8698d5317Sjmmv * purpose with or without fee is hereby granted, provided that the above
9698d5317Sjmmv * copyright notice and this permission notice appear in all copies.
10698d5317Sjmmv *
11698d5317Sjmmv * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12698d5317Sjmmv * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13698d5317Sjmmv * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14698d5317Sjmmv * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15698d5317Sjmmv * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16698d5317Sjmmv * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17698d5317Sjmmv * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18698d5317Sjmmv */
19698d5317Sjmmv
20698d5317Sjmmv #include <sys/types.h>
21698d5317Sjmmv
22698d5317Sjmmv #include <stdlib.h>
23698d5317Sjmmv
24698d5317Sjmmv #include "tmux.h"
25698d5317Sjmmv
26698d5317Sjmmv /*
27698d5317Sjmmv * The window layout is a tree of cells each of which can be one of: a
28698d5317Sjmmv * left-right container for a list of cells, a top-bottom container for a list
29698d5317Sjmmv * of cells, or a container for a window pane.
30698d5317Sjmmv *
31698d5317Sjmmv * Each window has a pointer to the root of its layout tree (containing its
32698d5317Sjmmv * panes), every pane has a pointer back to the cell containing it, and each
33698d5317Sjmmv * cell a pointer to its parent cell.
34698d5317Sjmmv */
35698d5317Sjmmv
364e179ddaSchristos static u_int layout_resize_check(struct window *, struct layout_cell *,
374e179ddaSchristos enum layout_type);
384e179ddaSchristos static int layout_resize_pane_grow(struct window *, struct layout_cell *,
394e179ddaSchristos enum layout_type, int, int);
404e179ddaSchristos static int layout_resize_pane_shrink(struct window *, struct layout_cell *,
414e179ddaSchristos enum layout_type, int);
424e179ddaSchristos static u_int layout_new_pane_size(struct window *, u_int,
434e179ddaSchristos struct layout_cell *, enum layout_type, u_int, u_int,
444e179ddaSchristos u_int);
454e179ddaSchristos static int layout_set_size_check(struct window *, struct layout_cell *,
464e179ddaSchristos enum layout_type, int);
474e179ddaSchristos static void layout_resize_child_cells(struct window *,
484e179ddaSchristos struct layout_cell *);
49698d5317Sjmmv
50698d5317Sjmmv struct layout_cell *
layout_create_cell(struct layout_cell * lcparent)51698d5317Sjmmv layout_create_cell(struct layout_cell *lcparent)
52698d5317Sjmmv {
53698d5317Sjmmv struct layout_cell *lc;
54698d5317Sjmmv
55698d5317Sjmmv lc = xmalloc(sizeof *lc);
56698d5317Sjmmv lc->type = LAYOUT_WINDOWPANE;
57698d5317Sjmmv lc->parent = lcparent;
58698d5317Sjmmv
59698d5317Sjmmv TAILQ_INIT(&lc->cells);
60698d5317Sjmmv
61698d5317Sjmmv lc->sx = UINT_MAX;
62698d5317Sjmmv lc->sy = UINT_MAX;
63698d5317Sjmmv
64698d5317Sjmmv lc->xoff = UINT_MAX;
65698d5317Sjmmv lc->yoff = UINT_MAX;
66698d5317Sjmmv
67698d5317Sjmmv lc->wp = NULL;
68698d5317Sjmmv
69698d5317Sjmmv return (lc);
70698d5317Sjmmv }
71698d5317Sjmmv
72698d5317Sjmmv void
layout_free_cell(struct layout_cell * lc)73698d5317Sjmmv layout_free_cell(struct layout_cell *lc)
74698d5317Sjmmv {
75698d5317Sjmmv struct layout_cell *lcchild;
76698d5317Sjmmv
77698d5317Sjmmv switch (lc->type) {
78698d5317Sjmmv case LAYOUT_LEFTRIGHT:
79698d5317Sjmmv case LAYOUT_TOPBOTTOM:
80698d5317Sjmmv while (!TAILQ_EMPTY(&lc->cells)) {
81698d5317Sjmmv lcchild = TAILQ_FIRST(&lc->cells);
82698d5317Sjmmv TAILQ_REMOVE(&lc->cells, lcchild, entry);
83698d5317Sjmmv layout_free_cell(lcchild);
84698d5317Sjmmv }
85698d5317Sjmmv break;
86698d5317Sjmmv case LAYOUT_WINDOWPANE:
87698d5317Sjmmv if (lc->wp != NULL)
88698d5317Sjmmv lc->wp->layout_cell = NULL;
89698d5317Sjmmv break;
90698d5317Sjmmv }
91698d5317Sjmmv
92928fc495Schristos free(lc);
93698d5317Sjmmv }
94698d5317Sjmmv
95698d5317Sjmmv void
layout_print_cell(struct layout_cell * lc,const char * hdr,u_int n)96698d5317Sjmmv layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
97698d5317Sjmmv {
98698d5317Sjmmv struct layout_cell *lcchild;
998f3b9483Schristos const char *type;
100698d5317Sjmmv
1018f3b9483Schristos switch (lc->type) {
1028f3b9483Schristos case LAYOUT_LEFTRIGHT:
1038f3b9483Schristos type = "LEFTRIGHT";
1048f3b9483Schristos break;
1058f3b9483Schristos case LAYOUT_TOPBOTTOM:
1068f3b9483Schristos type = "TOPBOTTOM";
1078f3b9483Schristos break;
1088f3b9483Schristos case LAYOUT_WINDOWPANE:
1098f3b9483Schristos type = "WINDOWPANE";
1108f3b9483Schristos break;
1118f3b9483Schristos default:
1128f3b9483Schristos type = "UNKNOWN";
1138f3b9483Schristos break;
1148f3b9483Schristos }
1158f3b9483Schristos log_debug("%s:%*s%p type %s [parent %p] wp=%p [%u,%u %ux%u]", hdr, n,
1168f3b9483Schristos " ", lc, type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx,
117ed4e6cd4Schristos lc->sy);
118698d5317Sjmmv switch (lc->type) {
119698d5317Sjmmv case LAYOUT_LEFTRIGHT:
120698d5317Sjmmv case LAYOUT_TOPBOTTOM:
121698d5317Sjmmv TAILQ_FOREACH(lcchild, &lc->cells, entry)
122698d5317Sjmmv layout_print_cell(lcchild, hdr, n + 1);
123698d5317Sjmmv break;
124698d5317Sjmmv case LAYOUT_WINDOWPANE:
125698d5317Sjmmv break;
126698d5317Sjmmv }
127698d5317Sjmmv }
128698d5317Sjmmv
1298f3b9483Schristos struct layout_cell *
layout_search_by_border(struct layout_cell * lc,u_int x,u_int y)1308f3b9483Schristos layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
1318f3b9483Schristos {
1328f3b9483Schristos struct layout_cell *lcchild, *last = NULL;
1338f3b9483Schristos
1348f3b9483Schristos TAILQ_FOREACH(lcchild, &lc->cells, entry) {
1358f3b9483Schristos if (x >= lcchild->xoff && x < lcchild->xoff + lcchild->sx &&
1368f3b9483Schristos y >= lcchild->yoff && y < lcchild->yoff + lcchild->sy) {
1378f3b9483Schristos /* Inside the cell - recurse. */
1388f3b9483Schristos return (layout_search_by_border(lcchild, x, y));
1398f3b9483Schristos }
1408f3b9483Schristos
1418f3b9483Schristos if (last == NULL) {
1428f3b9483Schristos last = lcchild;
1438f3b9483Schristos continue;
1448f3b9483Schristos }
1458f3b9483Schristos
1468f3b9483Schristos switch (lc->type) {
1478f3b9483Schristos case LAYOUT_LEFTRIGHT:
1488f3b9483Schristos if (x < lcchild->xoff && x >= last->xoff + last->sx)
1498f3b9483Schristos return (last);
1508f3b9483Schristos break;
1518f3b9483Schristos case LAYOUT_TOPBOTTOM:
1528f3b9483Schristos if (y < lcchild->yoff && y >= last->yoff + last->sy)
1538f3b9483Schristos return (last);
1548f3b9483Schristos break;
1558f3b9483Schristos case LAYOUT_WINDOWPANE:
1568f3b9483Schristos break;
1578f3b9483Schristos }
1588f3b9483Schristos
1598f3b9483Schristos last = lcchild;
1608f3b9483Schristos }
1618f3b9483Schristos
1628f3b9483Schristos return (NULL);
1638f3b9483Schristos }
1648f3b9483Schristos
165698d5317Sjmmv void
layout_set_size(struct layout_cell * lc,u_int sx,u_int sy,u_int xoff,u_int yoff)166ed4e6cd4Schristos layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff,
167ed4e6cd4Schristos u_int yoff)
168698d5317Sjmmv {
169698d5317Sjmmv lc->sx = sx;
170698d5317Sjmmv lc->sy = sy;
171698d5317Sjmmv
172698d5317Sjmmv lc->xoff = xoff;
173698d5317Sjmmv lc->yoff = yoff;
174698d5317Sjmmv }
175698d5317Sjmmv
176698d5317Sjmmv void
layout_make_leaf(struct layout_cell * lc,struct window_pane * wp)177698d5317Sjmmv layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
178698d5317Sjmmv {
179698d5317Sjmmv lc->type = LAYOUT_WINDOWPANE;
180698d5317Sjmmv
181698d5317Sjmmv TAILQ_INIT(&lc->cells);
182698d5317Sjmmv
183698d5317Sjmmv wp->layout_cell = lc;
184698d5317Sjmmv lc->wp = wp;
185698d5317Sjmmv }
186698d5317Sjmmv
187698d5317Sjmmv void
layout_make_node(struct layout_cell * lc,enum layout_type type)188698d5317Sjmmv layout_make_node(struct layout_cell *lc, enum layout_type type)
189698d5317Sjmmv {
190698d5317Sjmmv if (type == LAYOUT_WINDOWPANE)
191698d5317Sjmmv fatalx("bad layout type");
192698d5317Sjmmv lc->type = type;
193698d5317Sjmmv
194698d5317Sjmmv TAILQ_INIT(&lc->cells);
195698d5317Sjmmv
196698d5317Sjmmv if (lc->wp != NULL)
197698d5317Sjmmv lc->wp->layout_cell = NULL;
198698d5317Sjmmv lc->wp = NULL;
199698d5317Sjmmv }
200698d5317Sjmmv
2016483eba0Schristos /* Fix cell offsets for a child cell. */
2026483eba0Schristos static void
layout_fix_offsets1(struct layout_cell * lc)2036483eba0Schristos layout_fix_offsets1(struct layout_cell *lc)
204698d5317Sjmmv {
205698d5317Sjmmv struct layout_cell *lcchild;
206698d5317Sjmmv u_int xoff, yoff;
207698d5317Sjmmv
208698d5317Sjmmv if (lc->type == LAYOUT_LEFTRIGHT) {
209698d5317Sjmmv xoff = lc->xoff;
210698d5317Sjmmv TAILQ_FOREACH(lcchild, &lc->cells, entry) {
211698d5317Sjmmv lcchild->xoff = xoff;
212698d5317Sjmmv lcchild->yoff = lc->yoff;
213698d5317Sjmmv if (lcchild->type != LAYOUT_WINDOWPANE)
2146483eba0Schristos layout_fix_offsets1(lcchild);
215698d5317Sjmmv xoff += lcchild->sx + 1;
216698d5317Sjmmv }
217698d5317Sjmmv } else {
218698d5317Sjmmv yoff = lc->yoff;
219698d5317Sjmmv TAILQ_FOREACH(lcchild, &lc->cells, entry) {
220698d5317Sjmmv lcchild->xoff = lc->xoff;
221698d5317Sjmmv lcchild->yoff = yoff;
222698d5317Sjmmv if (lcchild->type != LAYOUT_WINDOWPANE)
2236483eba0Schristos layout_fix_offsets1(lcchild);
224698d5317Sjmmv yoff += lcchild->sy + 1;
225698d5317Sjmmv }
226698d5317Sjmmv }
227698d5317Sjmmv }
228698d5317Sjmmv
2296483eba0Schristos /* Update cell offsets based on their sizes. */
2306483eba0Schristos void
layout_fix_offsets(struct window * w)2316483eba0Schristos layout_fix_offsets(struct window *w)
2324e179ddaSchristos {
2336483eba0Schristos struct layout_cell *lc = w->layout_root;
2344e179ddaSchristos
2356483eba0Schristos lc->xoff = 0;
2366483eba0Schristos lc->yoff = 0;
2374e179ddaSchristos
2386483eba0Schristos layout_fix_offsets1(lc);
2396483eba0Schristos }
2406483eba0Schristos
2416483eba0Schristos /* Is this a top cell? */
2426483eba0Schristos static int
layout_cell_is_top(struct window * w,struct layout_cell * lc)2436483eba0Schristos layout_cell_is_top(struct window *w, struct layout_cell *lc)
2446483eba0Schristos {
2456483eba0Schristos struct layout_cell *next;
2466483eba0Schristos
2476483eba0Schristos while (lc != w->layout_root) {
2486483eba0Schristos next = lc->parent;
2496483eba0Schristos if (next->type == LAYOUT_TOPBOTTOM &&
2506483eba0Schristos lc != TAILQ_FIRST(&next->cells))
2514e179ddaSchristos return (0);
2526483eba0Schristos lc = next;
2534e179ddaSchristos }
2544e179ddaSchristos return (1);
2554e179ddaSchristos }
2564e179ddaSchristos
2576483eba0Schristos /* Is this a bottom cell? */
2586483eba0Schristos static int
layout_cell_is_bottom(struct window * w,struct layout_cell * lc)2596483eba0Schristos layout_cell_is_bottom(struct window *w, struct layout_cell *lc)
2606483eba0Schristos {
2616483eba0Schristos struct layout_cell *next;
2626483eba0Schristos
2636483eba0Schristos while (lc != w->layout_root) {
2646483eba0Schristos next = lc->parent;
2656483eba0Schristos if (next->type == LAYOUT_TOPBOTTOM &&
2666483eba0Schristos lc != TAILQ_LAST(&next->cells, layout_cells))
2676483eba0Schristos return (0);
2686483eba0Schristos lc = next;
2696483eba0Schristos }
2706483eba0Schristos return (1);
2716483eba0Schristos }
2726483eba0Schristos
2736483eba0Schristos /*
2746483eba0Schristos * Returns 1 if we need to add an extra line for the pane status line. This is
2756483eba0Schristos * the case for the most upper or lower panes only.
2766483eba0Schristos */
2776483eba0Schristos static int
layout_add_border(struct window * w,struct layout_cell * lc,int status)2786483eba0Schristos layout_add_border(struct window *w, struct layout_cell *lc, int status)
2796483eba0Schristos {
2806483eba0Schristos if (status == PANE_STATUS_TOP)
2816483eba0Schristos return (layout_cell_is_top(w, lc));
2826483eba0Schristos if (status == PANE_STATUS_BOTTOM)
2836483eba0Schristos return (layout_cell_is_bottom(w, lc));
2846483eba0Schristos return (0);
2856483eba0Schristos }
2866483eba0Schristos
287698d5317Sjmmv /* Update pane offsets and sizes based on their cells. */
288698d5317Sjmmv void
layout_fix_panes(struct window * w,struct window_pane * skip)289*9fb66d81Schristos layout_fix_panes(struct window *w, struct window_pane *skip)
290698d5317Sjmmv {
291698d5317Sjmmv struct window_pane *wp;
292698d5317Sjmmv struct layout_cell *lc;
2936483eba0Schristos int status;
294698d5317Sjmmv
2954e179ddaSchristos status = options_get_number(w->options, "pane-border-status");
296698d5317Sjmmv TAILQ_FOREACH(wp, &w->panes, entry) {
297*9fb66d81Schristos if ((lc = wp->layout_cell) == NULL || wp == skip)
298698d5317Sjmmv continue;
2994e179ddaSchristos
300698d5317Sjmmv wp->xoff = lc->xoff;
301698d5317Sjmmv wp->yoff = lc->yoff;
302698d5317Sjmmv
3036483eba0Schristos if (layout_add_border(w, lc, status)) {
3046483eba0Schristos if (status == PANE_STATUS_TOP)
3056483eba0Schristos wp->yoff++;
3066483eba0Schristos window_pane_resize(wp, lc->sx, lc->sy - 1);
3076483eba0Schristos } else
3086483eba0Schristos window_pane_resize(wp, lc->sx, lc->sy);
309698d5317Sjmmv }
310698d5317Sjmmv }
311698d5317Sjmmv
312698d5317Sjmmv /* Count the number of available cells in a layout. */
313698d5317Sjmmv u_int
layout_count_cells(struct layout_cell * lc)314698d5317Sjmmv layout_count_cells(struct layout_cell *lc)
315698d5317Sjmmv {
316698d5317Sjmmv struct layout_cell *lcchild;
3174e179ddaSchristos u_int count;
318698d5317Sjmmv
319698d5317Sjmmv switch (lc->type) {
320698d5317Sjmmv case LAYOUT_WINDOWPANE:
321698d5317Sjmmv return (1);
322698d5317Sjmmv case LAYOUT_LEFTRIGHT:
323698d5317Sjmmv case LAYOUT_TOPBOTTOM:
3244e179ddaSchristos count = 0;
325698d5317Sjmmv TAILQ_FOREACH(lcchild, &lc->cells, entry)
3264e179ddaSchristos count += layout_count_cells(lcchild);
3274e179ddaSchristos return (count);
328698d5317Sjmmv default:
329698d5317Sjmmv fatalx("bad layout type");
330698d5317Sjmmv }
331698d5317Sjmmv }
332698d5317Sjmmv
333698d5317Sjmmv /* Calculate how much size is available to be removed from a cell. */
3344e179ddaSchristos static u_int
layout_resize_check(struct window * w,struct layout_cell * lc,enum layout_type type)3354e179ddaSchristos layout_resize_check(struct window *w, struct layout_cell *lc,
3364e179ddaSchristos enum layout_type type)
337698d5317Sjmmv {
338698d5317Sjmmv struct layout_cell *lcchild;
339698d5317Sjmmv u_int available, minimum;
340ef36e747Schristos int status;
341698d5317Sjmmv
342ef36e747Schristos status = options_get_number(w->options, "pane-border-status");
343698d5317Sjmmv if (lc->type == LAYOUT_WINDOWPANE) {
344698d5317Sjmmv /* Space available in this cell only. */
3456483eba0Schristos if (type == LAYOUT_LEFTRIGHT) {
346698d5317Sjmmv available = lc->sx;
3476483eba0Schristos minimum = PANE_MINIMUM;
3486483eba0Schristos } else {
349698d5317Sjmmv available = lc->sy;
3506483eba0Schristos if (layout_add_border(w, lc, status))
3516483eba0Schristos minimum = PANE_MINIMUM + 1;
3526483eba0Schristos else
3536483eba0Schristos minimum = PANE_MINIMUM;
3544e179ddaSchristos }
3554e179ddaSchristos if (available > minimum)
3564e179ddaSchristos available -= minimum;
357698d5317Sjmmv else
358698d5317Sjmmv available = 0;
359698d5317Sjmmv } else if (lc->type == type) {
360698d5317Sjmmv /* Same type: total of available space in all child cells. */
361698d5317Sjmmv available = 0;
362698d5317Sjmmv TAILQ_FOREACH(lcchild, &lc->cells, entry)
3634e179ddaSchristos available += layout_resize_check(w, lcchild, type);
364698d5317Sjmmv } else {
365698d5317Sjmmv /* Different type: minimum of available space in child cells. */
366698d5317Sjmmv minimum = UINT_MAX;
367698d5317Sjmmv TAILQ_FOREACH(lcchild, &lc->cells, entry) {
3684e179ddaSchristos available = layout_resize_check(w, lcchild, type);
369698d5317Sjmmv if (available < minimum)
370698d5317Sjmmv minimum = available;
371698d5317Sjmmv }
372698d5317Sjmmv available = minimum;
373698d5317Sjmmv }
374698d5317Sjmmv
375698d5317Sjmmv return (available);
376698d5317Sjmmv }
377698d5317Sjmmv
378698d5317Sjmmv /*
379698d5317Sjmmv * Adjust cell size evenly, including altering its children. This function
380698d5317Sjmmv * expects the change to have already been bounded to the space available.
381698d5317Sjmmv */
382698d5317Sjmmv void
layout_resize_adjust(struct window * w,struct layout_cell * lc,enum layout_type type,int change)3834e179ddaSchristos layout_resize_adjust(struct window *w, struct layout_cell *lc,
3844e179ddaSchristos enum layout_type type, int change)
385698d5317Sjmmv {
386698d5317Sjmmv struct layout_cell *lcchild;
387698d5317Sjmmv
388698d5317Sjmmv /* Adjust the cell size. */
389698d5317Sjmmv if (type == LAYOUT_LEFTRIGHT)
390698d5317Sjmmv lc->sx += change;
391698d5317Sjmmv else
392698d5317Sjmmv lc->sy += change;
393698d5317Sjmmv
394698d5317Sjmmv /* If this is a leaf cell, that is all that is necessary. */
395698d5317Sjmmv if (type == LAYOUT_WINDOWPANE)
396698d5317Sjmmv return;
397698d5317Sjmmv
398698d5317Sjmmv /* Child cell runs in a different direction. */
399698d5317Sjmmv if (lc->type != type) {
400698d5317Sjmmv TAILQ_FOREACH(lcchild, &lc->cells, entry)
4014e179ddaSchristos layout_resize_adjust(w, lcchild, type, change);
402698d5317Sjmmv return;
403698d5317Sjmmv }
404698d5317Sjmmv
405698d5317Sjmmv /*
406698d5317Sjmmv * Child cell runs in the same direction. Adjust each child equally
407698d5317Sjmmv * until no further change is possible.
408698d5317Sjmmv */
409698d5317Sjmmv while (change != 0) {
410698d5317Sjmmv TAILQ_FOREACH(lcchild, &lc->cells, entry) {
411698d5317Sjmmv if (change == 0)
412698d5317Sjmmv break;
413698d5317Sjmmv if (change > 0) {
4144e179ddaSchristos layout_resize_adjust(w, lcchild, type, 1);
415698d5317Sjmmv change--;
416698d5317Sjmmv continue;
417698d5317Sjmmv }
4184e179ddaSchristos if (layout_resize_check(w, lcchild, type) > 0) {
4194e179ddaSchristos layout_resize_adjust(w, lcchild, type, -1);
420698d5317Sjmmv change++;
421698d5317Sjmmv }
422698d5317Sjmmv }
423698d5317Sjmmv }
424698d5317Sjmmv }
425698d5317Sjmmv
426698d5317Sjmmv /* Destroy a cell and redistribute the space. */
427698d5317Sjmmv void
layout_destroy_cell(struct window * w,struct layout_cell * lc,struct layout_cell ** lcroot)4284e179ddaSchristos layout_destroy_cell(struct window *w, struct layout_cell *lc,
4294e179ddaSchristos struct layout_cell **lcroot)
430698d5317Sjmmv {
431698d5317Sjmmv struct layout_cell *lcother, *lcparent;
432698d5317Sjmmv
433698d5317Sjmmv /*
434698d5317Sjmmv * If no parent, this is the last pane so window close is imminent and
435698d5317Sjmmv * there is no need to resize anything.
436698d5317Sjmmv */
437698d5317Sjmmv lcparent = lc->parent;
438698d5317Sjmmv if (lcparent == NULL) {
439698d5317Sjmmv layout_free_cell(lc);
440698d5317Sjmmv *lcroot = NULL;
441698d5317Sjmmv return;
442698d5317Sjmmv }
443698d5317Sjmmv
444698d5317Sjmmv /* Merge the space into the previous or next cell. */
445698d5317Sjmmv if (lc == TAILQ_FIRST(&lcparent->cells))
446698d5317Sjmmv lcother = TAILQ_NEXT(lc, entry);
447698d5317Sjmmv else
448698d5317Sjmmv lcother = TAILQ_PREV(lc, layout_cells, entry);
4496483eba0Schristos if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT)
4504e179ddaSchristos layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1);
4516483eba0Schristos else if (lcother != NULL)
4524e179ddaSchristos layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1);
453698d5317Sjmmv
454698d5317Sjmmv /* Remove this from the parent's list. */
455698d5317Sjmmv TAILQ_REMOVE(&lcparent->cells, lc, entry);
456698d5317Sjmmv layout_free_cell(lc);
457698d5317Sjmmv
458698d5317Sjmmv /*
459698d5317Sjmmv * If the parent now has one cell, remove the parent from the tree and
460698d5317Sjmmv * replace it by that cell.
461698d5317Sjmmv */
462698d5317Sjmmv lc = TAILQ_FIRST(&lcparent->cells);
463698d5317Sjmmv if (TAILQ_NEXT(lc, entry) == NULL) {
464698d5317Sjmmv TAILQ_REMOVE(&lcparent->cells, lc, entry);
465698d5317Sjmmv
466698d5317Sjmmv lc->parent = lcparent->parent;
467698d5317Sjmmv if (lc->parent == NULL) {
468698d5317Sjmmv lc->xoff = 0; lc->yoff = 0;
469698d5317Sjmmv *lcroot = lc;
470698d5317Sjmmv } else
471698d5317Sjmmv TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
472698d5317Sjmmv
473698d5317Sjmmv layout_free_cell(lcparent);
474698d5317Sjmmv }
475698d5317Sjmmv }
476698d5317Sjmmv
477698d5317Sjmmv void
layout_init(struct window * w,struct window_pane * wp)478928fc495Schristos layout_init(struct window *w, struct window_pane *wp)
479698d5317Sjmmv {
480698d5317Sjmmv struct layout_cell *lc;
481698d5317Sjmmv
482698d5317Sjmmv lc = w->layout_root = layout_create_cell(NULL);
483698d5317Sjmmv layout_set_size(lc, w->sx, w->sy, 0, 0);
484928fc495Schristos layout_make_leaf(lc, wp);
485*9fb66d81Schristos layout_fix_panes(w, NULL);
486698d5317Sjmmv }
487698d5317Sjmmv
488698d5317Sjmmv void
layout_free(struct window * w)489698d5317Sjmmv layout_free(struct window *w)
490698d5317Sjmmv {
491698d5317Sjmmv layout_free_cell(w->layout_root);
492698d5317Sjmmv }
493698d5317Sjmmv
494698d5317Sjmmv /* Resize the entire layout after window resize. */
495698d5317Sjmmv void
layout_resize(struct window * w,u_int sx,u_int sy)496698d5317Sjmmv layout_resize(struct window *w, u_int sx, u_int sy)
497698d5317Sjmmv {
498698d5317Sjmmv struct layout_cell *lc = w->layout_root;
499698d5317Sjmmv int xlimit, ylimit, xchange, ychange;
500698d5317Sjmmv
501698d5317Sjmmv /*
502698d5317Sjmmv * Adjust horizontally. Do not attempt to reduce the layout lower than
503698d5317Sjmmv * the minimum (more than the amount returned by layout_resize_check).
504698d5317Sjmmv *
505698d5317Sjmmv * This can mean that the window size is smaller than the total layout
506698d5317Sjmmv * size: redrawing this is handled at a higher level, but it does leave
507698d5317Sjmmv * a problem with growing the window size here: if the current size is
508698d5317Sjmmv * < the minimum, growing proportionately by adding to each pane is
509698d5317Sjmmv * wrong as it would keep the layout size larger than the window size.
510698d5317Sjmmv * Instead, spread the difference between the minimum and the new size
511698d5317Sjmmv * out proportionately - this should leave the layout fitting the new
512698d5317Sjmmv * window size.
513698d5317Sjmmv */
514ef36e747Schristos xchange = sx - lc->sx;
5154e179ddaSchristos xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
516698d5317Sjmmv if (xchange < 0 && xchange < -xlimit)
517698d5317Sjmmv xchange = -xlimit;
518698d5317Sjmmv if (xlimit == 0) {
519698d5317Sjmmv if (sx <= lc->sx) /* lc->sx is minimum possible */
520698d5317Sjmmv xchange = 0;
521698d5317Sjmmv else
522698d5317Sjmmv xchange = sx - lc->sx;
523698d5317Sjmmv }
524698d5317Sjmmv if (xchange != 0)
5254e179ddaSchristos layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, xchange);
526698d5317Sjmmv
527698d5317Sjmmv /* Adjust vertically in a similar fashion. */
528ef36e747Schristos ychange = sy - lc->sy;
5294e179ddaSchristos ylimit = layout_resize_check(w, lc, LAYOUT_TOPBOTTOM);
530698d5317Sjmmv if (ychange < 0 && ychange < -ylimit)
531698d5317Sjmmv ychange = -ylimit;
532698d5317Sjmmv if (ylimit == 0) {
533698d5317Sjmmv if (sy <= lc->sy) /* lc->sy is minimum possible */
534698d5317Sjmmv ychange = 0;
535698d5317Sjmmv else
536698d5317Sjmmv ychange = sy - lc->sy;
537698d5317Sjmmv }
538698d5317Sjmmv if (ychange != 0)
5394e179ddaSchristos layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange);
540698d5317Sjmmv
541698d5317Sjmmv /* Fix cell offsets. */
5426483eba0Schristos layout_fix_offsets(w);
543*9fb66d81Schristos layout_fix_panes(w, NULL);
544698d5317Sjmmv }
545698d5317Sjmmv
546928fc495Schristos /* Resize a pane to an absolute size. */
547928fc495Schristos void
layout_resize_pane_to(struct window_pane * wp,enum layout_type type,u_int new_size)548928fc495Schristos layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
549928fc495Schristos u_int new_size)
550928fc495Schristos {
551928fc495Schristos struct layout_cell *lc, *lcparent;
552928fc495Schristos int change, size;
553928fc495Schristos
554928fc495Schristos lc = wp->layout_cell;
555928fc495Schristos
556928fc495Schristos /* Find next parent of the same type. */
557928fc495Schristos lcparent = lc->parent;
558928fc495Schristos while (lcparent != NULL && lcparent->type != type) {
559928fc495Schristos lc = lcparent;
560928fc495Schristos lcparent = lc->parent;
561928fc495Schristos }
562928fc495Schristos if (lcparent == NULL)
563928fc495Schristos return;
564928fc495Schristos
565928fc495Schristos /* Work out the size adjustment. */
566928fc495Schristos if (type == LAYOUT_LEFTRIGHT)
567928fc495Schristos size = lc->sx;
568928fc495Schristos else
569928fc495Schristos size = lc->sy;
570928fc495Schristos if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
571928fc495Schristos change = size - new_size;
572928fc495Schristos else
573928fc495Schristos change = new_size - size;
574928fc495Schristos
575928fc495Schristos /* Resize the pane. */
5764e179ddaSchristos layout_resize_pane(wp, type, change, 1);
577928fc495Schristos }
578928fc495Schristos
579698d5317Sjmmv void
layout_resize_layout(struct window * w,struct layout_cell * lc,enum layout_type type,int change,int opposite)5808f3b9483Schristos layout_resize_layout(struct window *w, struct layout_cell *lc,
5818f3b9483Schristos enum layout_type type, int change, int opposite)
582698d5317Sjmmv {
583698d5317Sjmmv int needed, size;
584698d5317Sjmmv
585698d5317Sjmmv /* Grow or shrink the cell. */
586698d5317Sjmmv needed = change;
587698d5317Sjmmv while (needed != 0) {
588698d5317Sjmmv if (change > 0) {
5894e179ddaSchristos size = layout_resize_pane_grow(w, lc, type, needed,
5904e179ddaSchristos opposite);
591698d5317Sjmmv needed -= size;
592698d5317Sjmmv } else {
5934e179ddaSchristos size = layout_resize_pane_shrink(w, lc, type, needed);
594698d5317Sjmmv needed += size;
595698d5317Sjmmv }
596698d5317Sjmmv
597698d5317Sjmmv if (size == 0) /* no more change possible */
598698d5317Sjmmv break;
599698d5317Sjmmv }
600698d5317Sjmmv
601698d5317Sjmmv /* Fix cell offsets. */
6026483eba0Schristos layout_fix_offsets(w);
603*9fb66d81Schristos layout_fix_panes(w, NULL);
6048f3b9483Schristos notify_window("window-layout-changed", w);
6058f3b9483Schristos }
6068f3b9483Schristos
6078f3b9483Schristos /* Resize a single pane within the layout. */
6088f3b9483Schristos void
layout_resize_pane(struct window_pane * wp,enum layout_type type,int change,int opposite)6098f3b9483Schristos layout_resize_pane(struct window_pane *wp, enum layout_type type, int change,
6108f3b9483Schristos int opposite)
6118f3b9483Schristos {
6128f3b9483Schristos struct layout_cell *lc, *lcparent;
6138f3b9483Schristos
6148f3b9483Schristos lc = wp->layout_cell;
6158f3b9483Schristos
6168f3b9483Schristos /* Find next parent of the same type. */
6178f3b9483Schristos lcparent = lc->parent;
6188f3b9483Schristos while (lcparent != NULL && lcparent->type != type) {
6198f3b9483Schristos lc = lcparent;
6208f3b9483Schristos lcparent = lc->parent;
6218f3b9483Schristos }
6228f3b9483Schristos if (lcparent == NULL)
6238f3b9483Schristos return;
6248f3b9483Schristos
6258f3b9483Schristos /* If this is the last cell, move back one. */
6268f3b9483Schristos if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
6278f3b9483Schristos lc = TAILQ_PREV(lc, layout_cells, entry);
6288f3b9483Schristos
6298f3b9483Schristos layout_resize_layout(wp->window, lc, type, change, opposite);
630698d5317Sjmmv }
631698d5317Sjmmv
632928fc495Schristos /* Helper function to grow pane. */
6334e179ddaSchristos static int
layout_resize_pane_grow(struct window * w,struct layout_cell * lc,enum layout_type type,int needed,int opposite)6344e179ddaSchristos layout_resize_pane_grow(struct window *w, struct layout_cell *lc,
6354e179ddaSchristos enum layout_type type, int needed, int opposite)
636698d5317Sjmmv {
637698d5317Sjmmv struct layout_cell *lcadd, *lcremove;
6384e179ddaSchristos u_int size = 0;
639698d5317Sjmmv
640698d5317Sjmmv /* Growing. Always add to the current cell. */
641698d5317Sjmmv lcadd = lc;
642698d5317Sjmmv
643698d5317Sjmmv /* Look towards the tail for a suitable cell for reduction. */
644698d5317Sjmmv lcremove = TAILQ_NEXT(lc, entry);
645698d5317Sjmmv while (lcremove != NULL) {
6464e179ddaSchristos size = layout_resize_check(w, lcremove, type);
647698d5317Sjmmv if (size > 0)
648698d5317Sjmmv break;
649698d5317Sjmmv lcremove = TAILQ_NEXT(lcremove, entry);
650698d5317Sjmmv }
651698d5317Sjmmv
652698d5317Sjmmv /* If none found, look towards the head. */
6534e179ddaSchristos if (opposite && lcremove == NULL) {
654698d5317Sjmmv lcremove = TAILQ_PREV(lc, layout_cells, entry);
655698d5317Sjmmv while (lcremove != NULL) {
6564e179ddaSchristos size = layout_resize_check(w, lcremove, type);
657698d5317Sjmmv if (size > 0)
658698d5317Sjmmv break;
659698d5317Sjmmv lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
660698d5317Sjmmv }
6614e179ddaSchristos }
662698d5317Sjmmv if (lcremove == NULL)
663698d5317Sjmmv return (0);
664698d5317Sjmmv
665698d5317Sjmmv /* Change the cells. */
666698d5317Sjmmv if (size > (u_int) needed)
667698d5317Sjmmv size = needed;
6684e179ddaSchristos layout_resize_adjust(w, lcadd, type, size);
6694e179ddaSchristos layout_resize_adjust(w, lcremove, type, -size);
670698d5317Sjmmv return (size);
671698d5317Sjmmv }
672698d5317Sjmmv
673928fc495Schristos /* Helper function to shrink pane. */
6744e179ddaSchristos static int
layout_resize_pane_shrink(struct window * w,struct layout_cell * lc,enum layout_type type,int needed)6754e179ddaSchristos layout_resize_pane_shrink(struct window *w, struct layout_cell *lc,
6764e179ddaSchristos enum layout_type type, int needed)
677698d5317Sjmmv {
678698d5317Sjmmv struct layout_cell *lcadd, *lcremove;
679698d5317Sjmmv u_int size;
680698d5317Sjmmv
681698d5317Sjmmv /* Shrinking. Find cell to remove from by walking towards head. */
682698d5317Sjmmv lcremove = lc;
683698d5317Sjmmv do {
6844e179ddaSchristos size = layout_resize_check(w, lcremove, type);
685698d5317Sjmmv if (size != 0)
686698d5317Sjmmv break;
687698d5317Sjmmv lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
688698d5317Sjmmv } while (lcremove != NULL);
689698d5317Sjmmv if (lcremove == NULL)
690698d5317Sjmmv return (0);
691698d5317Sjmmv
692698d5317Sjmmv /* And add onto the next cell (from the original cell). */
693698d5317Sjmmv lcadd = TAILQ_NEXT(lc, entry);
694698d5317Sjmmv if (lcadd == NULL)
695698d5317Sjmmv return (0);
696698d5317Sjmmv
697698d5317Sjmmv /* Change the cells. */
698698d5317Sjmmv if (size > (u_int) -needed)
699698d5317Sjmmv size = -needed;
7004e179ddaSchristos layout_resize_adjust(w, lcadd, type, size);
7014e179ddaSchristos layout_resize_adjust(w, lcremove, type, -size);
702698d5317Sjmmv return (size);
703698d5317Sjmmv }
704698d5317Sjmmv
705698d5317Sjmmv /* Assign window pane to newly split cell. */
706698d5317Sjmmv void
layout_assign_pane(struct layout_cell * lc,struct window_pane * wp,int do_not_resize)707*9fb66d81Schristos layout_assign_pane(struct layout_cell *lc, struct window_pane *wp,
708*9fb66d81Schristos int do_not_resize)
709698d5317Sjmmv {
710698d5317Sjmmv layout_make_leaf(lc, wp);
711*9fb66d81Schristos if (do_not_resize)
712*9fb66d81Schristos layout_fix_panes(wp->window, wp);
713*9fb66d81Schristos else
714*9fb66d81Schristos layout_fix_panes(wp->window, NULL);
715698d5317Sjmmv }
716698d5317Sjmmv
7174e179ddaSchristos /* Calculate the new pane size for resized parent. */
7184e179ddaSchristos static u_int
layout_new_pane_size(struct window * w,u_int previous,struct layout_cell * lc,enum layout_type type,u_int size,u_int count_left,u_int size_left)7194e179ddaSchristos layout_new_pane_size(struct window *w, u_int previous, struct layout_cell *lc,
7204e179ddaSchristos enum layout_type type, u_int size, u_int count_left, u_int size_left)
7214e179ddaSchristos {
7224e179ddaSchristos u_int new_size, min, max, available;
7234e179ddaSchristos
7244e179ddaSchristos /* If this is the last cell, it can take all of the remaining size. */
7254e179ddaSchristos if (count_left == 1)
7264e179ddaSchristos return (size_left);
7274e179ddaSchristos
7284e179ddaSchristos /* How much is available in this parent? */
7294e179ddaSchristos available = layout_resize_check(w, lc, type);
7304e179ddaSchristos
7314e179ddaSchristos /*
7324e179ddaSchristos * Work out the minimum size of this cell and the new size
7334e179ddaSchristos * proportionate to the previous size.
7344e179ddaSchristos */
7354e179ddaSchristos min = (PANE_MINIMUM + 1) * (count_left - 1);
7364e179ddaSchristos if (type == LAYOUT_LEFTRIGHT) {
7374e179ddaSchristos if (lc->sx - available > min)
7384e179ddaSchristos min = lc->sx - available;
7394e179ddaSchristos new_size = (lc->sx * size) / previous;
7404e179ddaSchristos } else {
7414e179ddaSchristos if (lc->sy - available > min)
7424e179ddaSchristos min = lc->sy - available;
7434e179ddaSchristos new_size = (lc->sy * size) / previous;
7444e179ddaSchristos }
7454e179ddaSchristos
7464e179ddaSchristos /* Check against the maximum and minimum size. */
7474e179ddaSchristos max = size_left - min;
7484e179ddaSchristos if (new_size > max)
7494e179ddaSchristos new_size = max;
7504e179ddaSchristos if (new_size < PANE_MINIMUM)
7514e179ddaSchristos new_size = PANE_MINIMUM;
7524e179ddaSchristos return (new_size);
7534e179ddaSchristos }
7544e179ddaSchristos
7554e179ddaSchristos /* Check if the cell and all its children can be resized to a specific size. */
7564e179ddaSchristos static int
layout_set_size_check(struct window * w,struct layout_cell * lc,enum layout_type type,int size)7574e179ddaSchristos layout_set_size_check(struct window *w, struct layout_cell *lc,
7584e179ddaSchristos enum layout_type type, int size)
7594e179ddaSchristos {
7604e179ddaSchristos struct layout_cell *lcchild;
7614e179ddaSchristos u_int new_size, available, previous, count, idx;
7624e179ddaSchristos
7634e179ddaSchristos /* Cells with no children must just be bigger than minimum. */
7644e179ddaSchristos if (lc->type == LAYOUT_WINDOWPANE)
7654e179ddaSchristos return (size >= PANE_MINIMUM);
7664e179ddaSchristos available = size;
7674e179ddaSchristos
7684e179ddaSchristos /* Count number of children. */
7694e179ddaSchristos count = 0;
7704e179ddaSchristos TAILQ_FOREACH(lcchild, &lc->cells, entry)
7714e179ddaSchristos count++;
7724e179ddaSchristos
7734e179ddaSchristos /* Check new size will work for each child. */
7744e179ddaSchristos if (lc->type == type) {
775ef36e747Schristos if (available < (count * 2) - 1)
776ef36e747Schristos return (0);
777ef36e747Schristos
7784e179ddaSchristos if (type == LAYOUT_LEFTRIGHT)
7794e179ddaSchristos previous = lc->sx;
7804e179ddaSchristos else
7814e179ddaSchristos previous = lc->sy;
7824e179ddaSchristos
7834e179ddaSchristos idx = 0;
7844e179ddaSchristos TAILQ_FOREACH(lcchild, &lc->cells, entry) {
7854e179ddaSchristos new_size = layout_new_pane_size(w, previous, lcchild,
7864e179ddaSchristos type, size, count - idx, available);
787ef36e747Schristos if (idx == count - 1) {
7884e179ddaSchristos if (new_size > available)
7894e179ddaSchristos return (0);
790ef36e747Schristos available -= new_size;
791ef36e747Schristos } else {
792ef36e747Schristos if (new_size + 1 > available)
793ef36e747Schristos return (0);
794ef36e747Schristos available -= new_size + 1;
795ef36e747Schristos }
7964e179ddaSchristos if (!layout_set_size_check(w, lcchild, type, new_size))
7974e179ddaSchristos return (0);
7984e179ddaSchristos idx++;
7994e179ddaSchristos }
8004e179ddaSchristos } else {
8014e179ddaSchristos TAILQ_FOREACH(lcchild, &lc->cells, entry) {
8024e179ddaSchristos if (lcchild->type == LAYOUT_WINDOWPANE)
8034e179ddaSchristos continue;
8044e179ddaSchristos if (!layout_set_size_check(w, lcchild, type, size))
8054e179ddaSchristos return (0);
8064e179ddaSchristos }
8074e179ddaSchristos }
8084e179ddaSchristos
8094e179ddaSchristos return (1);
8104e179ddaSchristos }
8114e179ddaSchristos
8124e179ddaSchristos /* Resize all child cells to fit within the current cell. */
8134e179ddaSchristos static void
layout_resize_child_cells(struct window * w,struct layout_cell * lc)8144e179ddaSchristos layout_resize_child_cells(struct window *w, struct layout_cell *lc)
8154e179ddaSchristos {
8164e179ddaSchristos struct layout_cell *lcchild;
8174e179ddaSchristos u_int previous, available, count, idx;
8184e179ddaSchristos
8194e179ddaSchristos if (lc->type == LAYOUT_WINDOWPANE)
8204e179ddaSchristos return;
8214e179ddaSchristos
8224e179ddaSchristos /* What is the current size used? */
8234e179ddaSchristos count = 0;
8244e179ddaSchristos previous = 0;
8254e179ddaSchristos TAILQ_FOREACH(lcchild, &lc->cells, entry) {
8264e179ddaSchristos count++;
8274e179ddaSchristos if (lc->type == LAYOUT_LEFTRIGHT)
8284e179ddaSchristos previous += lcchild->sx;
8294e179ddaSchristos else if (lc->type == LAYOUT_TOPBOTTOM)
8304e179ddaSchristos previous += lcchild->sy;
8314e179ddaSchristos }
8324e179ddaSchristos previous += (count - 1);
8334e179ddaSchristos
8344e179ddaSchristos /* And how much is available? */
8354e179ddaSchristos available = 0;
8364e179ddaSchristos if (lc->type == LAYOUT_LEFTRIGHT)
8374e179ddaSchristos available = lc->sx;
8384e179ddaSchristos else if (lc->type == LAYOUT_TOPBOTTOM)
8394e179ddaSchristos available = lc->sy;
8404e179ddaSchristos
8414e179ddaSchristos /* Resize children into the new size. */
8424e179ddaSchristos idx = 0;
8434e179ddaSchristos TAILQ_FOREACH(lcchild, &lc->cells, entry) {
8444e179ddaSchristos if (lc->type == LAYOUT_TOPBOTTOM) {
8454e179ddaSchristos lcchild->sx = lc->sx;
8464e179ddaSchristos lcchild->xoff = lc->xoff;
8474e179ddaSchristos } else {
8484e179ddaSchristos lcchild->sx = layout_new_pane_size(w, previous, lcchild,
8494e179ddaSchristos lc->type, lc->sx, count - idx, available);
8504e179ddaSchristos available -= (lcchild->sx + 1);
8514e179ddaSchristos }
8524e179ddaSchristos if (lc->type == LAYOUT_LEFTRIGHT)
8534e179ddaSchristos lcchild->sy = lc->sy;
8544e179ddaSchristos else {
8554e179ddaSchristos lcchild->sy = layout_new_pane_size(w, previous, lcchild,
8564e179ddaSchristos lc->type, lc->sy, count - idx, available);
8574e179ddaSchristos available -= (lcchild->sy + 1);
8584e179ddaSchristos }
8594e179ddaSchristos layout_resize_child_cells(w, lcchild);
8604e179ddaSchristos idx++;
8614e179ddaSchristos }
8624e179ddaSchristos }
8634e179ddaSchristos
864698d5317Sjmmv /*
865698d5317Sjmmv * Split a pane into two. size is a hint, or -1 for default half/half
866698d5317Sjmmv * split. This must be followed by layout_assign_pane before much else happens!
8674e179ddaSchristos */
868698d5317Sjmmv struct layout_cell *
layout_split_pane(struct window_pane * wp,enum layout_type type,int size,int flags)869ed4e6cd4Schristos layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
8706483eba0Schristos int flags)
871698d5317Sjmmv {
872928fc495Schristos struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
873ef36e747Schristos u_int sx, sy, xoff, yoff, size1, size2, minimum;
8744e179ddaSchristos u_int new_size, saved_size, resize_first = 0;
8756483eba0Schristos int full_size = (flags & SPAWN_FULLSIZE), status;
876698d5317Sjmmv
8774e179ddaSchristos /*
8784e179ddaSchristos * If full_size is specified, add a new cell at the top of the window
8794e179ddaSchristos * layout. Otherwise, split the cell for the current pane.
8804e179ddaSchristos */
8814e179ddaSchristos if (full_size)
8824e179ddaSchristos lc = wp->window->layout_root;
8834e179ddaSchristos else
884698d5317Sjmmv lc = wp->layout_cell;
885ef36e747Schristos status = options_get_number(wp->window->options, "pane-border-status");
886698d5317Sjmmv
887698d5317Sjmmv /* Copy the old cell size. */
888698d5317Sjmmv sx = lc->sx;
889698d5317Sjmmv sy = lc->sy;
890698d5317Sjmmv xoff = lc->xoff;
891698d5317Sjmmv yoff = lc->yoff;
892698d5317Sjmmv
893698d5317Sjmmv /* Check there is enough space for the two new panes. */
894698d5317Sjmmv switch (type) {
895698d5317Sjmmv case LAYOUT_LEFTRIGHT:
896698d5317Sjmmv if (sx < PANE_MINIMUM * 2 + 1)
897698d5317Sjmmv return (NULL);
898698d5317Sjmmv break;
899698d5317Sjmmv case LAYOUT_TOPBOTTOM:
9006483eba0Schristos if (layout_add_border(wp->window, lc, status))
9016483eba0Schristos minimum = PANE_MINIMUM * 2 + 2;
9026483eba0Schristos else
903ef36e747Schristos minimum = PANE_MINIMUM * 2 + 1;
904ef36e747Schristos if (sy < minimum)
905698d5317Sjmmv return (NULL);
906698d5317Sjmmv break;
907698d5317Sjmmv default:
908698d5317Sjmmv fatalx("bad layout type");
909698d5317Sjmmv }
910698d5317Sjmmv
9114e179ddaSchristos /*
9124e179ddaSchristos * Calculate new cell sizes. size is the target size or -1 for middle
9134e179ddaSchristos * split, size1 is the size of the top/left and size2 the bottom/right.
9144e179ddaSchristos */
9154e179ddaSchristos if (type == LAYOUT_LEFTRIGHT)
9164e179ddaSchristos saved_size = sx;
9174e179ddaSchristos else
9184e179ddaSchristos saved_size = sy;
9194e179ddaSchristos if (size < 0)
9204e179ddaSchristos size2 = ((saved_size + 1) / 2) - 1;
9216483eba0Schristos else if (flags & SPAWN_BEFORE)
9224e179ddaSchristos size2 = saved_size - size - 1;
9234e179ddaSchristos else
9244e179ddaSchristos size2 = size;
9254e179ddaSchristos if (size2 < PANE_MINIMUM)
9264e179ddaSchristos size2 = PANE_MINIMUM;
9274e179ddaSchristos else if (size2 > saved_size - 2)
9284e179ddaSchristos size2 = saved_size - 2;
9294e179ddaSchristos size1 = saved_size - 1 - size2;
9304e179ddaSchristos
9314e179ddaSchristos /* Which size are we using? */
9326483eba0Schristos if (flags & SPAWN_BEFORE)
9334e179ddaSchristos new_size = size2;
9344e179ddaSchristos else
9354e179ddaSchristos new_size = size1;
9364e179ddaSchristos
9374e179ddaSchristos /* Confirm there is enough space for full size pane. */
9384e179ddaSchristos if (full_size && !layout_set_size_check(wp->window, lc, type, new_size))
9394e179ddaSchristos return (NULL);
9404e179ddaSchristos
941698d5317Sjmmv if (lc->parent != NULL && lc->parent->type == type) {
942698d5317Sjmmv /*
943698d5317Sjmmv * If the parent exists and is of the same type as the split,
944698d5317Sjmmv * create a new cell and insert it after this one.
945698d5317Sjmmv */
946928fc495Schristos lcparent = lc->parent;
947928fc495Schristos lcnew = layout_create_cell(lcparent);
9486483eba0Schristos if (flags & SPAWN_BEFORE)
949928fc495Schristos TAILQ_INSERT_BEFORE(lc, lcnew, entry);
950928fc495Schristos else
951928fc495Schristos TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
9524e179ddaSchristos } else if (full_size && lc->parent == NULL && lc->type == type) {
9534e179ddaSchristos /*
9544e179ddaSchristos * If the new full size pane is the same type as the root
9554e179ddaSchristos * split, insert the new pane under the existing root cell
9564e179ddaSchristos * instead of creating a new root cell. The existing layout
9574e179ddaSchristos * must be resized before inserting the new cell.
9584e179ddaSchristos */
9594e179ddaSchristos if (lc->type == LAYOUT_LEFTRIGHT) {
9604e179ddaSchristos lc->sx = new_size;
9614e179ddaSchristos layout_resize_child_cells(wp->window, lc);
9624e179ddaSchristos lc->sx = saved_size;
9634e179ddaSchristos } else if (lc->type == LAYOUT_TOPBOTTOM) {
9644e179ddaSchristos lc->sy = new_size;
9654e179ddaSchristos layout_resize_child_cells(wp->window, lc);
9664e179ddaSchristos lc->sy = saved_size;
9674e179ddaSchristos }
9684e179ddaSchristos resize_first = 1;
9694e179ddaSchristos
9704e179ddaSchristos /* Create the new cell. */
9714e179ddaSchristos lcnew = layout_create_cell(lc);
9724e179ddaSchristos size = saved_size - 1 - new_size;
9734e179ddaSchristos if (lc->type == LAYOUT_LEFTRIGHT)
9744e179ddaSchristos layout_set_size(lcnew, size, sy, 0, 0);
9754e179ddaSchristos else if (lc->type == LAYOUT_TOPBOTTOM)
9764e179ddaSchristos layout_set_size(lcnew, sx, size, 0, 0);
9776483eba0Schristos if (flags & SPAWN_BEFORE)
9784e179ddaSchristos TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry);
9794e179ddaSchristos else
9804e179ddaSchristos TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
981698d5317Sjmmv } else {
982698d5317Sjmmv /*
983698d5317Sjmmv * Otherwise create a new parent and insert it.
984698d5317Sjmmv */
985698d5317Sjmmv
986698d5317Sjmmv /* Create and insert the replacement parent. */
987698d5317Sjmmv lcparent = layout_create_cell(lc->parent);
988698d5317Sjmmv layout_make_node(lcparent, type);
989698d5317Sjmmv layout_set_size(lcparent, sx, sy, xoff, yoff);
990698d5317Sjmmv if (lc->parent == NULL)
991698d5317Sjmmv wp->window->layout_root = lcparent;
992698d5317Sjmmv else
993698d5317Sjmmv TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
994698d5317Sjmmv
995698d5317Sjmmv /* Insert the old cell. */
996698d5317Sjmmv lc->parent = lcparent;
997698d5317Sjmmv TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
998698d5317Sjmmv
999698d5317Sjmmv /* Create the new child cell. */
1000698d5317Sjmmv lcnew = layout_create_cell(lcparent);
10016483eba0Schristos if (flags & SPAWN_BEFORE)
1002928fc495Schristos TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry);
1003928fc495Schristos else
1004698d5317Sjmmv TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
1005698d5317Sjmmv }
10066483eba0Schristos if (flags & SPAWN_BEFORE) {
1007928fc495Schristos lc1 = lcnew;
1008928fc495Schristos lc2 = lc;
1009928fc495Schristos } else {
1010928fc495Schristos lc1 = lc;
1011928fc495Schristos lc2 = lcnew;
1012928fc495Schristos }
1013698d5317Sjmmv
10144e179ddaSchristos /*
10154e179ddaSchristos * Set new cell sizes. size1 is the size of the top/left and size2 the
10164e179ddaSchristos * bottom/right.
1017698d5317Sjmmv */
10184e179ddaSchristos if (!resize_first && type == LAYOUT_LEFTRIGHT) {
1019928fc495Schristos layout_set_size(lc1, size1, sy, xoff, yoff);
1020928fc495Schristos layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff);
10214e179ddaSchristos } else if (!resize_first && type == LAYOUT_TOPBOTTOM) {
1022928fc495Schristos layout_set_size(lc1, sx, size1, xoff, yoff);
1023928fc495Schristos layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1);
1024698d5317Sjmmv }
10254e179ddaSchristos if (full_size) {
10264e179ddaSchristos if (!resize_first)
10274e179ddaSchristos layout_resize_child_cells(wp->window, lc);
10286483eba0Schristos layout_fix_offsets(wp->window);
10294e179ddaSchristos } else
1030698d5317Sjmmv layout_make_leaf(lc, wp);
1031698d5317Sjmmv
1032698d5317Sjmmv return (lcnew);
1033698d5317Sjmmv }
1034698d5317Sjmmv
1035698d5317Sjmmv /* Destroy the cell associated with a pane. */
1036698d5317Sjmmv void
layout_close_pane(struct window_pane * wp)1037698d5317Sjmmv layout_close_pane(struct window_pane *wp)
1038698d5317Sjmmv {
10394e179ddaSchristos struct window *w = wp->window;
10404e179ddaSchristos
1041698d5317Sjmmv /* Remove the cell. */
10424e179ddaSchristos layout_destroy_cell(w, wp->layout_cell, &w->layout_root);
1043698d5317Sjmmv
1044698d5317Sjmmv /* Fix pane offsets and sizes. */
10454e179ddaSchristos if (w->layout_root != NULL) {
10466483eba0Schristos layout_fix_offsets(w);
1047*9fb66d81Schristos layout_fix_panes(w, NULL);
1048698d5317Sjmmv }
10494e179ddaSchristos notify_window("window-layout-changed", w);
1050698d5317Sjmmv }
10518f3b9483Schristos
10528f3b9483Schristos int
layout_spread_cell(struct window * w,struct layout_cell * parent)10538f3b9483Schristos layout_spread_cell(struct window *w, struct layout_cell *parent)
10548f3b9483Schristos {
10558f3b9483Schristos struct layout_cell *lc;
1056ef36e747Schristos u_int number, each, size, this;
1057ef36e747Schristos int change, changed, status;
10588f3b9483Schristos
10598f3b9483Schristos number = 0;
10608f3b9483Schristos TAILQ_FOREACH (lc, &parent->cells, entry)
10618f3b9483Schristos number++;
10628f3b9483Schristos if (number <= 1)
10638f3b9483Schristos return (0);
1064ef36e747Schristos status = options_get_number(w->options, "pane-border-status");
10658f3b9483Schristos
10668f3b9483Schristos if (parent->type == LAYOUT_LEFTRIGHT)
10678f3b9483Schristos size = parent->sx;
1068ef36e747Schristos else if (parent->type == LAYOUT_TOPBOTTOM) {
10696483eba0Schristos if (layout_add_border(w, parent, status))
10706483eba0Schristos size = parent->sy - 1;
10716483eba0Schristos else
10728f3b9483Schristos size = parent->sy;
1073ef36e747Schristos } else
1074ef36e747Schristos return (0);
1075ef36e747Schristos if (size < number - 1)
10768f3b9483Schristos return (0);
10778f3b9483Schristos each = (size - (number - 1)) / number;
1078ef36e747Schristos if (each == 0)
1079ef36e747Schristos return (0);
10808f3b9483Schristos
10818f3b9483Schristos changed = 0;
10828f3b9483Schristos TAILQ_FOREACH (lc, &parent->cells, entry) {
10838f3b9483Schristos if (TAILQ_NEXT(lc, entry) == NULL)
10848f3b9483Schristos each = size - ((each + 1) * (number - 1));
10858f3b9483Schristos change = 0;
10868f3b9483Schristos if (parent->type == LAYOUT_LEFTRIGHT) {
10878f3b9483Schristos change = each - (int)lc->sx;
10888f3b9483Schristos layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
10898f3b9483Schristos } else if (parent->type == LAYOUT_TOPBOTTOM) {
10906483eba0Schristos if (layout_add_border(w, lc, status))
10916483eba0Schristos this = each + 1;
10926483eba0Schristos else
1093ef36e747Schristos this = each;
1094ef36e747Schristos change = this - (int)lc->sy;
10958f3b9483Schristos layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
10968f3b9483Schristos }
10978f3b9483Schristos if (change != 0)
10988f3b9483Schristos changed = 1;
10998f3b9483Schristos }
11008f3b9483Schristos return (changed);
11018f3b9483Schristos }
11028f3b9483Schristos
11038f3b9483Schristos void
layout_spread_out(struct window_pane * wp)11048f3b9483Schristos layout_spread_out(struct window_pane *wp)
11058f3b9483Schristos {
11068f3b9483Schristos struct layout_cell *parent;
11078f3b9483Schristos struct window *w = wp->window;
11088f3b9483Schristos
11098f3b9483Schristos parent = wp->layout_cell->parent;
11108f3b9483Schristos if (parent == NULL)
11118f3b9483Schristos return;
11128f3b9483Schristos
11138f3b9483Schristos do {
11148f3b9483Schristos if (layout_spread_cell(w, parent)) {
11156483eba0Schristos layout_fix_offsets(w);
1116*9fb66d81Schristos layout_fix_panes(w, NULL);
11178f3b9483Schristos break;
11188f3b9483Schristos }
11198f3b9483Schristos } while ((parent = parent->parent) != NULL);
11208f3b9483Schristos }
1121