xref: /netbsd-src/external/bsd/tmux/dist/layout.c (revision 9fb66d812c00ebfb445c0b47dea128f32aa6fe96)
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