xref: /openbsd/usr.bin/tmux/layout-set.c (revision cd409aae)
1*cd409aaeSnicm /* $OpenBSD: layout-set.c,v 1.32 2024/08/23 10:19:06 nicm Exp $ */
2af9e4c5dSnicm 
3af9e4c5dSnicm /*
498ca8272Snicm  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
5af9e4c5dSnicm  *
6af9e4c5dSnicm  * Permission to use, copy, modify, and distribute this software for any
7af9e4c5dSnicm  * purpose with or without fee is hereby granted, provided that the above
8af9e4c5dSnicm  * copyright notice and this permission notice appear in all copies.
9af9e4c5dSnicm  *
10af9e4c5dSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11af9e4c5dSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12af9e4c5dSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13af9e4c5dSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14af9e4c5dSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15af9e4c5dSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16af9e4c5dSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17af9e4c5dSnicm  */
18af9e4c5dSnicm 
19af9e4c5dSnicm #include <sys/types.h>
20af9e4c5dSnicm 
21c6e6a0b3Snicm #include <stdlib.h>
22af9e4c5dSnicm #include <string.h>
23af9e4c5dSnicm 
24af9e4c5dSnicm #include "tmux.h"
25af9e4c5dSnicm 
26af9e4c5dSnicm /*
27bfcd10e2Snicm  * Set window layouts - predefined methods to arrange windows. These are
28bfcd10e2Snicm  * one-off and generate a layout tree.
29af9e4c5dSnicm  */
30af9e4c5dSnicm 
319883b791Snicm static void	layout_set_even_h(struct window *);
329883b791Snicm static void	layout_set_even_v(struct window *);
339883b791Snicm static void	layout_set_main_h(struct window *);
3414aabaa7Snicm static void	layout_set_main_h_mirrored(struct window *);
359883b791Snicm static void	layout_set_main_v(struct window *);
3614aabaa7Snicm static void	layout_set_main_v_mirrored(struct window *);
379883b791Snicm static void	layout_set_tiled(struct window *);
38af9e4c5dSnicm 
39c97fab4eSnicm static const struct {
40af9e4c5dSnicm 	const char	*name;
41af9e4c5dSnicm 	void	      	(*arrange)(struct window *);
42af9e4c5dSnicm } layout_sets[] = {
43af9e4c5dSnicm 	{ "even-horizontal", layout_set_even_h },
44af9e4c5dSnicm 	{ "even-vertical", layout_set_even_v },
45af9e4c5dSnicm 	{ "main-horizontal", layout_set_main_h },
4614aabaa7Snicm 	{ "main-horizontal-mirrored", layout_set_main_h_mirrored },
47af9e4c5dSnicm 	{ "main-vertical", layout_set_main_v },
4814aabaa7Snicm 	{ "main-vertical-mirrored", layout_set_main_v_mirrored },
49fcae69d1Snicm 	{ "tiled", layout_set_tiled },
50af9e4c5dSnicm };
51af9e4c5dSnicm 
52af9e4c5dSnicm int
layout_set_lookup(const char * name)53af9e4c5dSnicm layout_set_lookup(const char *name)
54af9e4c5dSnicm {
55af9e4c5dSnicm 	u_int	i;
56af9e4c5dSnicm 	int	matched = -1;
57af9e4c5dSnicm 
58af9e4c5dSnicm 	for (i = 0; i < nitems(layout_sets); i++) {
59*cd409aaeSnicm 		if (strcmp(layout_sets[i].name, name) == 0)
60*cd409aaeSnicm 			return (i);
61*cd409aaeSnicm 	}
62*cd409aaeSnicm 	for (i = 0; i < nitems(layout_sets); i++) {
63af9e4c5dSnicm 		if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
64af9e4c5dSnicm 			if (matched != -1)	/* ambiguous */
65af9e4c5dSnicm 				return (-1);
66af9e4c5dSnicm 			matched = i;
67af9e4c5dSnicm 		}
68af9e4c5dSnicm 	}
69af9e4c5dSnicm 
70af9e4c5dSnicm 	return (matched);
71af9e4c5dSnicm }
72af9e4c5dSnicm 
73af9e4c5dSnicm u_int
layout_set_select(struct window * w,u_int layout)74af9e4c5dSnicm layout_set_select(struct window *w, u_int layout)
75af9e4c5dSnicm {
76af9e4c5dSnicm 	if (layout > nitems(layout_sets) - 1)
77af9e4c5dSnicm 		layout = nitems(layout_sets) - 1;
78af9e4c5dSnicm 
79af9e4c5dSnicm 	if (layout_sets[layout].arrange != NULL)
80af9e4c5dSnicm 		layout_sets[layout].arrange(w);
81af9e4c5dSnicm 
82ba17146dSnicm 	w->lastlayout = layout;
83af9e4c5dSnicm 	return (layout);
84af9e4c5dSnicm }
85af9e4c5dSnicm 
86af9e4c5dSnicm u_int
layout_set_next(struct window * w)87af9e4c5dSnicm layout_set_next(struct window *w)
88af9e4c5dSnicm {
89ba17146dSnicm 	u_int	layout;
90ba17146dSnicm 
91ba17146dSnicm 	if (w->lastlayout == -1)
92ba17146dSnicm 		layout = 0;
93ba17146dSnicm 	else {
94ba17146dSnicm 		layout = w->lastlayout + 1;
95ba17146dSnicm 		if (layout > nitems(layout_sets) - 1)
96ba17146dSnicm 			layout = 0;
97ba17146dSnicm 	}
98af9e4c5dSnicm 
99af9e4c5dSnicm 	if (layout_sets[layout].arrange != NULL)
100af9e4c5dSnicm 		layout_sets[layout].arrange(w);
101ba17146dSnicm 	w->lastlayout = layout;
102af9e4c5dSnicm 	return (layout);
103af9e4c5dSnicm }
104af9e4c5dSnicm 
105af9e4c5dSnicm u_int
layout_set_previous(struct window * w)106af9e4c5dSnicm layout_set_previous(struct window *w)
107af9e4c5dSnicm {
108ba17146dSnicm 	u_int	layout;
109ba17146dSnicm 
110ba17146dSnicm 	if (w->lastlayout == -1)
111ba17146dSnicm 		layout = nitems(layout_sets) - 1;
112ba17146dSnicm 	else {
113ba17146dSnicm 		layout = w->lastlayout;
114ba17146dSnicm 		if (layout == 0)
115ba17146dSnicm 			layout = nitems(layout_sets) - 1;
116ba17146dSnicm 		else
117ba17146dSnicm 			layout--;
118ba17146dSnicm 	}
119af9e4c5dSnicm 
120af9e4c5dSnicm 	if (layout_sets[layout].arrange != NULL)
121af9e4c5dSnicm 		layout_sets[layout].arrange(w);
122ba17146dSnicm 	w->lastlayout = layout;
123af9e4c5dSnicm 	return (layout);
124af9e4c5dSnicm }
125af9e4c5dSnicm 
1269883b791Snicm static void
layout_set_even(struct window * w,enum layout_type type)127967ee5b9Snicm layout_set_even(struct window *w, enum layout_type type)
128af9e4c5dSnicm {
129af9e4c5dSnicm 	struct window_pane	*wp;
130af9e4c5dSnicm 	struct layout_cell	*lc, *lcnew;
131d4ddf7e1Snicm 	u_int			 n, sx, sy;
132af9e4c5dSnicm 
133af9e4c5dSnicm 	layout_print_cell(w->layout_root, __func__, 1);
134af9e4c5dSnicm 
135af9e4c5dSnicm 	/* Get number of panes. */
136af9e4c5dSnicm 	n = window_count_panes(w);
137af9e4c5dSnicm 	if (n <= 1)
138af9e4c5dSnicm 		return;
139af9e4c5dSnicm 
140af9e4c5dSnicm 	/* Free the old root and construct a new. */
141af9e4c5dSnicm 	layout_free(w);
142af9e4c5dSnicm 	lc = w->layout_root = layout_create_cell(NULL);
143d4ddf7e1Snicm 	if (type == LAYOUT_LEFTRIGHT) {
144d4ddf7e1Snicm 		sx = (n * (PANE_MINIMUM + 1)) - 1;
145d4ddf7e1Snicm 		if (sx < w->sx)
146d4ddf7e1Snicm 			sx = w->sx;
147d4ddf7e1Snicm 		sy = w->sy;
148d4ddf7e1Snicm 	} else {
149d4ddf7e1Snicm 		sy = (n * (PANE_MINIMUM + 1)) - 1;
150d4ddf7e1Snicm 		if (sy < w->sy)
151d4ddf7e1Snicm 			sy = w->sy;
152d4ddf7e1Snicm 		sx = w->sx;
153d4ddf7e1Snicm 	}
154d4ddf7e1Snicm 	layout_set_size(lc, sx, sy, 0, 0);
155967ee5b9Snicm 	layout_make_node(lc, type);
156af9e4c5dSnicm 
157af9e4c5dSnicm 	/* Build new leaf cells. */
158af9e4c5dSnicm 	TAILQ_FOREACH(wp, &w->panes, entry) {
159af9e4c5dSnicm 		lcnew = layout_create_cell(lc);
160af9e4c5dSnicm 		layout_make_leaf(lcnew, wp);
1618135e028Snicm 		lcnew->sx = w->sx;
1628135e028Snicm 		lcnew->sy = w->sy;
163af9e4c5dSnicm 		TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
164af9e4c5dSnicm 	}
165af9e4c5dSnicm 
166967ee5b9Snicm 	/* Spread out cells. */
167967ee5b9Snicm 	layout_spread_cell(w, lc);
168af9e4c5dSnicm 
169af9e4c5dSnicm 	/* Fix cell offsets. */
17042fbd26aSnicm 	layout_fix_offsets(w);
171baddd6b2Snicm 	layout_fix_panes(w, NULL);
172af9e4c5dSnicm 
173af9e4c5dSnicm 	layout_print_cell(w->layout_root, __func__, 1);
174af9e4c5dSnicm 
1754a8b0ea5Snicm 	window_resize(w, lc->sx, lc->sy, -1, -1);
1764d154873Snicm 	notify_window("window-layout-changed", w);
177af9e4c5dSnicm 	server_redraw_window(w);
178af9e4c5dSnicm }
179af9e4c5dSnicm 
1809883b791Snicm static void
layout_set_even_h(struct window * w)181967ee5b9Snicm layout_set_even_h(struct window *w)
182967ee5b9Snicm {
183967ee5b9Snicm 	layout_set_even(w, LAYOUT_LEFTRIGHT);
184967ee5b9Snicm }
185967ee5b9Snicm 
186967ee5b9Snicm static void
layout_set_even_v(struct window * w)187af9e4c5dSnicm layout_set_even_v(struct window *w)
188af9e4c5dSnicm {
189967ee5b9Snicm 	layout_set_even(w, LAYOUT_TOPBOTTOM);
190af9e4c5dSnicm }
191af9e4c5dSnicm 
1929883b791Snicm static void
layout_set_main_h(struct window * w)193af9e4c5dSnicm layout_set_main_h(struct window *w)
194af9e4c5dSnicm {
195af9e4c5dSnicm 	struct window_pane	*wp;
19606f48543Snicm 	struct layout_cell	*lc, *lcmain, *lcother, *lcchild;
197439fb663Snicm 	u_int			 n, mainh, otherh, sx, sy;
198c6e6a0b3Snicm 	char			*cause;
199c6e6a0b3Snicm 	const char		*s;
200af9e4c5dSnicm 
201af9e4c5dSnicm 	layout_print_cell(w->layout_root, __func__, 1);
202af9e4c5dSnicm 
203af9e4c5dSnicm 	/* Get number of panes. */
204af9e4c5dSnicm 	n = window_count_panes(w);
205af9e4c5dSnicm 	if (n <= 1)
206af9e4c5dSnicm 		return;
207af9e4c5dSnicm 	n--;	/* take off main pane */
208af9e4c5dSnicm 
209439fb663Snicm 	/* Find available height - take off one line for the border. */
210439fb663Snicm 	sy = w->sy - 1;
211439fb663Snicm 
212c6e6a0b3Snicm 	/* Get the main pane height. */
213c6e6a0b3Snicm 	s = options_get_string(w->options, "main-pane-height");
214c6e6a0b3Snicm 	mainh = args_string_percentage(s, 0, sy, sy, &cause);
215c6e6a0b3Snicm 	if (cause != NULL) {
216c6e6a0b3Snicm 		mainh = 24;
217c6e6a0b3Snicm 		free(cause);
218c6e6a0b3Snicm 	}
219c6e6a0b3Snicm 
220c6e6a0b3Snicm 	/* Work out the other pane height. */
221439fb663Snicm 	if (mainh + PANE_MINIMUM >= sy) {
222439fb663Snicm 		if (sy <= PANE_MINIMUM + PANE_MINIMUM)
22306f48543Snicm 			mainh = PANE_MINIMUM;
224af9e4c5dSnicm 		else
225439fb663Snicm 			mainh = sy - PANE_MINIMUM;
22606f48543Snicm 		otherh = PANE_MINIMUM;
22706f48543Snicm 	} else {
228c6e6a0b3Snicm 		s = options_get_string(w->options, "other-pane-height");
229c6e6a0b3Snicm 		otherh = args_string_percentage(s, 0, sy, sy, &cause);
230c6e6a0b3Snicm 		if (cause != NULL || otherh == 0) {
231439fb663Snicm 			otherh = sy - mainh;
232c6e6a0b3Snicm 			free(cause);
233c6e6a0b3Snicm 		} else if (otherh > sy || sy - otherh < mainh)
234439fb663Snicm 			otherh = sy - mainh;
23506f48543Snicm 		else
236439fb663Snicm 			mainh = sy - otherh;
23706f48543Snicm 	}
23806f48543Snicm 
23910d8425bSnicm 	/* Work out what width is needed. */
24006f48543Snicm 	sx = (n * (PANE_MINIMUM + 1)) - 1;
24106f48543Snicm 	if (sx < w->sx)
24206f48543Snicm 		sx = w->sx;
243af9e4c5dSnicm 
244af9e4c5dSnicm 	/* Free old tree and create a new root. */
245af9e4c5dSnicm 	layout_free(w);
246af9e4c5dSnicm 	lc = w->layout_root = layout_create_cell(NULL);
247439fb663Snicm 	layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
248af9e4c5dSnicm 	layout_make_node(lc, LAYOUT_TOPBOTTOM);
249af9e4c5dSnicm 
250af9e4c5dSnicm 	/* Create the main pane. */
251af9e4c5dSnicm 	lcmain = layout_create_cell(lc);
25206f48543Snicm 	layout_set_size(lcmain, sx, mainh, 0, 0);
253af9e4c5dSnicm 	layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
254af9e4c5dSnicm 	TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
255af9e4c5dSnicm 
25606f48543Snicm 	/* Create the other pane. */
25706f48543Snicm 	lcother = layout_create_cell(lc);
25806f48543Snicm 	layout_set_size(lcother, sx, otherh, 0, 0);
25910d8425bSnicm 	if (n == 1) {
26010d8425bSnicm 		wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
26110d8425bSnicm 		layout_make_leaf(lcother, wp);
26210d8425bSnicm 		TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
26310d8425bSnicm 	} else {
26406f48543Snicm 		layout_make_node(lcother, LAYOUT_LEFTRIGHT);
26506f48543Snicm 		TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
266af9e4c5dSnicm 
26706f48543Snicm 		/* Add the remaining panes as children. */
26806f48543Snicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
26906f48543Snicm 			if (wp == TAILQ_FIRST(&w->panes))
270af9e4c5dSnicm 				continue;
27110d8425bSnicm 			lcchild = layout_create_cell(lcother);
27206f48543Snicm 			layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
273af9e4c5dSnicm 			layout_make_leaf(lcchild, wp);
27406f48543Snicm 			TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
275af9e4c5dSnicm 		}
27606f48543Snicm 		layout_spread_cell(w, lcother);
27710d8425bSnicm 	}
278af9e4c5dSnicm 
279af9e4c5dSnicm 	/* Fix cell offsets. */
28042fbd26aSnicm 	layout_fix_offsets(w);
281baddd6b2Snicm 	layout_fix_panes(w, NULL);
282af9e4c5dSnicm 
283af9e4c5dSnicm 	layout_print_cell(w->layout_root, __func__, 1);
284af9e4c5dSnicm 
2854a8b0ea5Snicm 	window_resize(w, lc->sx, lc->sy, -1, -1);
2864d154873Snicm 	notify_window("window-layout-changed", w);
287af9e4c5dSnicm 	server_redraw_window(w);
288af9e4c5dSnicm }
289af9e4c5dSnicm 
2909883b791Snicm static void
layout_set_main_h_mirrored(struct window * w)29114aabaa7Snicm layout_set_main_h_mirrored(struct window *w)
29214aabaa7Snicm {
29314aabaa7Snicm 	struct window_pane	*wp;
29414aabaa7Snicm 	struct layout_cell	*lc, *lcmain, *lcother, *lcchild;
29514aabaa7Snicm 	u_int			 n, mainh, otherh, sx, sy;
29614aabaa7Snicm 	char			*cause;
29714aabaa7Snicm 	const char		*s;
29814aabaa7Snicm 
29914aabaa7Snicm 	layout_print_cell(w->layout_root, __func__, 1);
30014aabaa7Snicm 
30114aabaa7Snicm 	/* Get number of panes. */
30214aabaa7Snicm 	n = window_count_panes(w);
30314aabaa7Snicm 	if (n <= 1)
30414aabaa7Snicm 		return;
30514aabaa7Snicm 	n--;	/* take off main pane */
30614aabaa7Snicm 
30714aabaa7Snicm 	/* Find available height - take off one line for the border. */
30814aabaa7Snicm 	sy = w->sy - 1;
30914aabaa7Snicm 
31014aabaa7Snicm 	/* Get the main pane height. */
31114aabaa7Snicm 	s = options_get_string(w->options, "main-pane-height");
31214aabaa7Snicm 	mainh = args_string_percentage(s, 0, sy, sy, &cause);
31314aabaa7Snicm 	if (cause != NULL) {
31414aabaa7Snicm 		mainh = 24;
31514aabaa7Snicm 		free(cause);
31614aabaa7Snicm 	}
31714aabaa7Snicm 
31814aabaa7Snicm 	/* Work out the other pane height. */
31914aabaa7Snicm 	if (mainh + PANE_MINIMUM >= sy) {
32014aabaa7Snicm 		if (sy <= PANE_MINIMUM + PANE_MINIMUM)
32114aabaa7Snicm 			mainh = PANE_MINIMUM;
32214aabaa7Snicm 		else
32314aabaa7Snicm 			mainh = sy - PANE_MINIMUM;
32414aabaa7Snicm 		otherh = PANE_MINIMUM;
32514aabaa7Snicm 	} else {
32614aabaa7Snicm 		s = options_get_string(w->options, "other-pane-height");
32714aabaa7Snicm 		otherh = args_string_percentage(s, 0, sy, sy, &cause);
32814aabaa7Snicm 		if (cause != NULL || otherh == 0) {
32914aabaa7Snicm 			otherh = sy - mainh;
33014aabaa7Snicm 			free(cause);
33114aabaa7Snicm 		} else if (otherh > sy || sy - otherh < mainh)
33214aabaa7Snicm 			otherh = sy - mainh;
33314aabaa7Snicm 		else
33414aabaa7Snicm 			mainh = sy - otherh;
33514aabaa7Snicm 	}
33614aabaa7Snicm 
33714aabaa7Snicm 	/* Work out what width is needed. */
33814aabaa7Snicm 	sx = (n * (PANE_MINIMUM + 1)) - 1;
33914aabaa7Snicm 	if (sx < w->sx)
34014aabaa7Snicm 		sx = w->sx;
34114aabaa7Snicm 
34214aabaa7Snicm 	/* Free old tree and create a new root. */
34314aabaa7Snicm 	layout_free(w);
34414aabaa7Snicm 	lc = w->layout_root = layout_create_cell(NULL);
34514aabaa7Snicm 	layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
34614aabaa7Snicm 	layout_make_node(lc, LAYOUT_TOPBOTTOM);
34714aabaa7Snicm 
34814aabaa7Snicm 	/* Create the other pane. */
34914aabaa7Snicm 	lcother = layout_create_cell(lc);
35014aabaa7Snicm 	layout_set_size(lcother, sx, otherh, 0, 0);
35114aabaa7Snicm 	if (n == 1) {
35214aabaa7Snicm 		wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
35314aabaa7Snicm 		layout_make_leaf(lcother, wp);
35414aabaa7Snicm 		TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
35514aabaa7Snicm 	} else {
35614aabaa7Snicm 		layout_make_node(lcother, LAYOUT_LEFTRIGHT);
35714aabaa7Snicm 		TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
35814aabaa7Snicm 
35914aabaa7Snicm 		/* Add the remaining panes as children. */
36014aabaa7Snicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
36114aabaa7Snicm 			if (wp == TAILQ_FIRST(&w->panes))
36214aabaa7Snicm 				continue;
36314aabaa7Snicm 			lcchild = layout_create_cell(lcother);
36414aabaa7Snicm 			layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
36514aabaa7Snicm 			layout_make_leaf(lcchild, wp);
36614aabaa7Snicm 			TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
36714aabaa7Snicm 		}
36814aabaa7Snicm 		layout_spread_cell(w, lcother);
36914aabaa7Snicm 	}
37014aabaa7Snicm 
37114aabaa7Snicm 	/* Create the main pane. */
37214aabaa7Snicm 	lcmain = layout_create_cell(lc);
37314aabaa7Snicm 	layout_set_size(lcmain, sx, mainh, 0, 0);
37414aabaa7Snicm 	layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
37514aabaa7Snicm 	TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
37614aabaa7Snicm 
37714aabaa7Snicm 	/* Fix cell offsets. */
37814aabaa7Snicm 	layout_fix_offsets(w);
37914aabaa7Snicm 	layout_fix_panes(w, NULL);
38014aabaa7Snicm 
38114aabaa7Snicm 	layout_print_cell(w->layout_root, __func__, 1);
38214aabaa7Snicm 
38314aabaa7Snicm 	window_resize(w, lc->sx, lc->sy, -1, -1);
38414aabaa7Snicm 	notify_window("window-layout-changed", w);
38514aabaa7Snicm 	server_redraw_window(w);
38614aabaa7Snicm }
38714aabaa7Snicm 
38814aabaa7Snicm static void
layout_set_main_v(struct window * w)389af9e4c5dSnicm layout_set_main_v(struct window *w)
390af9e4c5dSnicm {
391af9e4c5dSnicm 	struct window_pane	*wp;
39206f48543Snicm 	struct layout_cell	*lc, *lcmain, *lcother, *lcchild;
393439fb663Snicm 	u_int			 n, mainw, otherw, sx, sy;
394c6e6a0b3Snicm 	char			*cause;
395c6e6a0b3Snicm 	const char		*s;
396af9e4c5dSnicm 
397af9e4c5dSnicm 	layout_print_cell(w->layout_root, __func__, 1);
398af9e4c5dSnicm 
399af9e4c5dSnicm 	/* Get number of panes. */
400af9e4c5dSnicm 	n = window_count_panes(w);
401af9e4c5dSnicm 	if (n <= 1)
402af9e4c5dSnicm 		return;
403af9e4c5dSnicm 	n--;	/* take off main pane */
404af9e4c5dSnicm 
405439fb663Snicm 	/* Find available width - take off one line for the border. */
406439fb663Snicm 	sx = w->sx - 1;
407439fb663Snicm 
408c6e6a0b3Snicm 	/* Get the main pane width. */
409c6e6a0b3Snicm 	s = options_get_string(w->options, "main-pane-width");
410c6e6a0b3Snicm 	mainw = args_string_percentage(s, 0, sx, sx, &cause);
411c6e6a0b3Snicm 	if (cause != NULL) {
412c6e6a0b3Snicm 		mainw = 80;
413c6e6a0b3Snicm 		free(cause);
414c6e6a0b3Snicm 	}
415c6e6a0b3Snicm 
416c6e6a0b3Snicm 	/* Work out the other pane width. */
417439fb663Snicm 	if (mainw + PANE_MINIMUM >= sx) {
418439fb663Snicm 		if (sx <= PANE_MINIMUM + PANE_MINIMUM)
41906f48543Snicm 			mainw = PANE_MINIMUM;
420af9e4c5dSnicm 		else
421439fb663Snicm 			mainw = sx - PANE_MINIMUM;
42206f48543Snicm 		otherw = PANE_MINIMUM;
42306f48543Snicm 	} else {
424c6e6a0b3Snicm 		s = options_get_string(w->options, "other-pane-width");
425c6e6a0b3Snicm 		otherw = args_string_percentage(s, 0, sx, sx, &cause);
426c6e6a0b3Snicm 		if (cause != NULL || otherw == 0) {
427439fb663Snicm 			otherw = sx - mainw;
428c6e6a0b3Snicm 			free(cause);
429c6e6a0b3Snicm 		} else if (otherw > sx || sx - otherw < mainw)
430439fb663Snicm 			otherw = sx - mainw;
43106f48543Snicm 		else
432439fb663Snicm 			mainw = sx - otherw;
43306f48543Snicm 	}
43406f48543Snicm 
43506f48543Snicm 	/* Work out what height is needed. */
43606f48543Snicm 	sy = (n * (PANE_MINIMUM + 1)) - 1;
43706f48543Snicm 	if (sy < w->sy)
43806f48543Snicm 		sy = w->sy;
439af9e4c5dSnicm 
440af9e4c5dSnicm 	/* Free old tree and create a new root. */
441af9e4c5dSnicm 	layout_free(w);
442af9e4c5dSnicm 	lc = w->layout_root = layout_create_cell(NULL);
443439fb663Snicm 	layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
444af9e4c5dSnicm 	layout_make_node(lc, LAYOUT_LEFTRIGHT);
445af9e4c5dSnicm 
446af9e4c5dSnicm 	/* Create the main pane. */
447af9e4c5dSnicm 	lcmain = layout_create_cell(lc);
44806f48543Snicm 	layout_set_size(lcmain, mainw, sy, 0, 0);
449af9e4c5dSnicm 	layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
450af9e4c5dSnicm 	TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
451af9e4c5dSnicm 
45206f48543Snicm 	/* Create the other pane. */
45306f48543Snicm 	lcother = layout_create_cell(lc);
45406f48543Snicm 	layout_set_size(lcother, otherw, sy, 0, 0);
45510d8425bSnicm 	if (n == 1) {
45610d8425bSnicm 		wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
45710d8425bSnicm 		layout_make_leaf(lcother, wp);
45810d8425bSnicm 		TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
45910d8425bSnicm 	} else {
46006f48543Snicm 		layout_make_node(lcother, LAYOUT_TOPBOTTOM);
46106f48543Snicm 		TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
462af9e4c5dSnicm 
46306f48543Snicm 		/* Add the remaining panes as children. */
46406f48543Snicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
46506f48543Snicm 			if (wp == TAILQ_FIRST(&w->panes))
466af9e4c5dSnicm 				continue;
46710d8425bSnicm 			lcchild = layout_create_cell(lcother);
46806f48543Snicm 			layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
469af9e4c5dSnicm 			layout_make_leaf(lcchild, wp);
47006f48543Snicm 			TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
471af9e4c5dSnicm 		}
47206f48543Snicm 		layout_spread_cell(w, lcother);
47310d8425bSnicm 	}
474af9e4c5dSnicm 
475af9e4c5dSnicm 	/* Fix cell offsets. */
47642fbd26aSnicm 	layout_fix_offsets(w);
477baddd6b2Snicm 	layout_fix_panes(w, NULL);
478af9e4c5dSnicm 
479af9e4c5dSnicm 	layout_print_cell(w->layout_root, __func__, 1);
480af9e4c5dSnicm 
4814a8b0ea5Snicm 	window_resize(w, lc->sx, lc->sy, -1, -1);
4824d154873Snicm 	notify_window("window-layout-changed", w);
483af9e4c5dSnicm 	server_redraw_window(w);
484af9e4c5dSnicm }
485fcae69d1Snicm 
48614aabaa7Snicm static void
layout_set_main_v_mirrored(struct window * w)48714aabaa7Snicm layout_set_main_v_mirrored(struct window *w)
48814aabaa7Snicm {
48914aabaa7Snicm 	struct window_pane	*wp;
49014aabaa7Snicm 	struct layout_cell	*lc, *lcmain, *lcother, *lcchild;
49114aabaa7Snicm 	u_int			 n, mainw, otherw, sx, sy;
49214aabaa7Snicm 	char			*cause;
49314aabaa7Snicm 	const char		*s;
49414aabaa7Snicm 
49514aabaa7Snicm 	layout_print_cell(w->layout_root, __func__, 1);
49614aabaa7Snicm 
49714aabaa7Snicm 	/* Get number of panes. */
49814aabaa7Snicm 	n = window_count_panes(w);
49914aabaa7Snicm 	if (n <= 1)
50014aabaa7Snicm 		return;
50114aabaa7Snicm 	n--;	/* take off main pane */
50214aabaa7Snicm 
50314aabaa7Snicm 	/* Find available width - take off one line for the border. */
50414aabaa7Snicm 	sx = w->sx - 1;
50514aabaa7Snicm 
50614aabaa7Snicm 	/* Get the main pane width. */
50714aabaa7Snicm 	s = options_get_string(w->options, "main-pane-width");
50814aabaa7Snicm 	mainw = args_string_percentage(s, 0, sx, sx, &cause);
50914aabaa7Snicm 	if (cause != NULL) {
51014aabaa7Snicm 		mainw = 80;
51114aabaa7Snicm 		free(cause);
51214aabaa7Snicm 	}
51314aabaa7Snicm 
51414aabaa7Snicm 	/* Work out the other pane width. */
51514aabaa7Snicm 	if (mainw + PANE_MINIMUM >= sx) {
51614aabaa7Snicm 		if (sx <= PANE_MINIMUM + PANE_MINIMUM)
51714aabaa7Snicm 			mainw = PANE_MINIMUM;
51814aabaa7Snicm 		else
51914aabaa7Snicm 			mainw = sx - PANE_MINIMUM;
52014aabaa7Snicm 		otherw = PANE_MINIMUM;
52114aabaa7Snicm 	} else {
52214aabaa7Snicm 		s = options_get_string(w->options, "other-pane-width");
52314aabaa7Snicm 		otherw = args_string_percentage(s, 0, sx, sx, &cause);
52414aabaa7Snicm 		if (cause != NULL || otherw == 0) {
52514aabaa7Snicm 			otherw = sx - mainw;
52614aabaa7Snicm 			free(cause);
52714aabaa7Snicm 		} else if (otherw > sx || sx - otherw < mainw)
52814aabaa7Snicm 			otherw = sx - mainw;
52914aabaa7Snicm 		else
53014aabaa7Snicm 			mainw = sx - otherw;
53114aabaa7Snicm 	}
53214aabaa7Snicm 
53314aabaa7Snicm 	/* Work out what height is needed. */
53414aabaa7Snicm 	sy = (n * (PANE_MINIMUM + 1)) - 1;
53514aabaa7Snicm 	if (sy < w->sy)
53614aabaa7Snicm 		sy = w->sy;
53714aabaa7Snicm 
53814aabaa7Snicm 	/* Free old tree and create a new root. */
53914aabaa7Snicm 	layout_free(w);
54014aabaa7Snicm 	lc = w->layout_root = layout_create_cell(NULL);
54114aabaa7Snicm 	layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
54214aabaa7Snicm 	layout_make_node(lc, LAYOUT_LEFTRIGHT);
54314aabaa7Snicm 
54414aabaa7Snicm 	/* Create the other pane. */
54514aabaa7Snicm 	lcother = layout_create_cell(lc);
54614aabaa7Snicm 	layout_set_size(lcother, otherw, sy, 0, 0);
54714aabaa7Snicm 	if (n == 1) {
54814aabaa7Snicm 		wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
54914aabaa7Snicm 		layout_make_leaf(lcother, wp);
55014aabaa7Snicm 		TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
55114aabaa7Snicm 	} else {
55214aabaa7Snicm 		layout_make_node(lcother, LAYOUT_TOPBOTTOM);
55314aabaa7Snicm 		TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
55414aabaa7Snicm 
55514aabaa7Snicm 		/* Add the remaining panes as children. */
55614aabaa7Snicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
55714aabaa7Snicm 			if (wp == TAILQ_FIRST(&w->panes))
55814aabaa7Snicm 				continue;
55914aabaa7Snicm 			lcchild = layout_create_cell(lcother);
56014aabaa7Snicm 			layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
56114aabaa7Snicm 			layout_make_leaf(lcchild, wp);
56214aabaa7Snicm 			TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
56314aabaa7Snicm 		}
56414aabaa7Snicm 		layout_spread_cell(w, lcother);
56514aabaa7Snicm 	}
56614aabaa7Snicm 
56714aabaa7Snicm 	/* Create the main pane. */
56814aabaa7Snicm 	lcmain = layout_create_cell(lc);
56914aabaa7Snicm 	layout_set_size(lcmain, mainw, sy, 0, 0);
57014aabaa7Snicm 	layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
57114aabaa7Snicm 	TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
57214aabaa7Snicm 
57314aabaa7Snicm 	/* Fix cell offsets. */
57414aabaa7Snicm 	layout_fix_offsets(w);
57514aabaa7Snicm 	layout_fix_panes(w, NULL);
57614aabaa7Snicm 
57714aabaa7Snicm 	layout_print_cell(w->layout_root, __func__, 1);
57814aabaa7Snicm 
57914aabaa7Snicm 	window_resize(w, lc->sx, lc->sy, -1, -1);
58014aabaa7Snicm 	notify_window("window-layout-changed", w);
58114aabaa7Snicm 	server_redraw_window(w);
58214aabaa7Snicm }
58314aabaa7Snicm 
584fcae69d1Snicm void
layout_set_tiled(struct window * w)585fcae69d1Snicm layout_set_tiled(struct window *w)
586fcae69d1Snicm {
587fcae69d1Snicm 	struct window_pane	*wp;
588fcae69d1Snicm 	struct layout_cell	*lc, *lcrow, *lcchild;
589d4ddf7e1Snicm 	u_int			 n, width, height, used, sx, sy;
590fcae69d1Snicm 	u_int			 i, j, columns, rows;
591fcae69d1Snicm 
592fcae69d1Snicm 	layout_print_cell(w->layout_root, __func__, 1);
593fcae69d1Snicm 
594fcae69d1Snicm 	/* Get number of panes. */
595fcae69d1Snicm 	n = window_count_panes(w);
596fcae69d1Snicm 	if (n <= 1)
597fcae69d1Snicm 		return;
598fcae69d1Snicm 
599fcae69d1Snicm 	/* How many rows and columns are wanted? */
600fcae69d1Snicm 	rows = columns = 1;
601fcae69d1Snicm 	while (rows * columns < n) {
602fcae69d1Snicm 		rows++;
603fcae69d1Snicm 		if (rows * columns < n)
604fcae69d1Snicm 			columns++;
605fcae69d1Snicm 	}
606fcae69d1Snicm 
607fcae69d1Snicm 	/* What width and height should they be? */
608b2c857acSnicm 	width = (w->sx - (columns - 1)) / columns;
609b2c857acSnicm 	if (width < PANE_MINIMUM)
610b2c857acSnicm 		width = PANE_MINIMUM;
611b2c857acSnicm 	height = (w->sy - (rows - 1)) / rows;
612b2c857acSnicm 	if (height < PANE_MINIMUM)
613b2c857acSnicm 		height = PANE_MINIMUM;
614fcae69d1Snicm 
615fcae69d1Snicm 	/* Free old tree and create a new root. */
616fcae69d1Snicm 	layout_free(w);
617fcae69d1Snicm 	lc = w->layout_root = layout_create_cell(NULL);
618d4ddf7e1Snicm 	sx = ((width + 1) * columns) - 1;
619d4ddf7e1Snicm 	if (sx < w->sx)
620d4ddf7e1Snicm 		sx = w->sx;
621d4ddf7e1Snicm 	sy = ((height + 1) * rows) - 1;
622d4ddf7e1Snicm 	if (sy < w->sy)
623d4ddf7e1Snicm 		sy = w->sy;
624d4ddf7e1Snicm 	layout_set_size(lc, sx, sy, 0, 0);
625fcae69d1Snicm 	layout_make_node(lc, LAYOUT_TOPBOTTOM);
626fcae69d1Snicm 
627fcae69d1Snicm 	/* Create a grid of the cells. */
628fcae69d1Snicm 	wp = TAILQ_FIRST(&w->panes);
629fcae69d1Snicm 	for (j = 0; j < rows; j++) {
630fcae69d1Snicm 		/* If this is the last cell, all done. */
631fcae69d1Snicm 		if (wp == NULL)
632fcae69d1Snicm 			break;
633fcae69d1Snicm 
634fcae69d1Snicm 		/* Create the new row. */
635fcae69d1Snicm 		lcrow = layout_create_cell(lc);
636b2c857acSnicm 		layout_set_size(lcrow, w->sx, height, 0, 0);
637fcae69d1Snicm 		TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
638fcae69d1Snicm 
639fcae69d1Snicm 		/* If only one column, just use the row directly. */
640e09b5663Snicm 		if (n - (j * columns) == 1 || columns == 1) {
641fcae69d1Snicm 			layout_make_leaf(lcrow, wp);
642fcae69d1Snicm 			wp = TAILQ_NEXT(wp, entry);
643fcae69d1Snicm 			continue;
644fcae69d1Snicm 		}
645fcae69d1Snicm 
646fcae69d1Snicm 		/* Add in the columns. */
647fcae69d1Snicm 		layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
648fcae69d1Snicm 		for (i = 0; i < columns; i++) {
649fcae69d1Snicm 			/* Create and add a pane cell. */
650fcae69d1Snicm 			lcchild = layout_create_cell(lcrow);
651b2c857acSnicm 			layout_set_size(lcchild, width, height, 0, 0);
652fcae69d1Snicm 			layout_make_leaf(lcchild, wp);
653fcae69d1Snicm 			TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
654fcae69d1Snicm 
655fcae69d1Snicm 			/* Move to the next cell. */
656fcae69d1Snicm 			if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
657fcae69d1Snicm 				break;
658fcae69d1Snicm 		}
659fcae69d1Snicm 
660fcae69d1Snicm 		/*
661fcae69d1Snicm 		 * Adjust the row and columns to fit the full width if
662fcae69d1Snicm 		 * necessary.
663fcae69d1Snicm 		 */
664fcae69d1Snicm 		if (i == columns)
665fcae69d1Snicm 			i--;
666b2c857acSnicm 		used = ((i + 1) * (width + 1)) - 1;
667fcae69d1Snicm 		if (w->sx <= used)
668fcae69d1Snicm 			continue;
669fcae69d1Snicm 		lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
67007b91187Snicm 		layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT,
67107b91187Snicm 		    w->sx - used);
672fcae69d1Snicm 	}
673fcae69d1Snicm 
674fcae69d1Snicm 	/* Adjust the last row height to fit if necessary. */
675b2c857acSnicm 	used = (rows * height) + rows - 1;
676fcae69d1Snicm 	if (w->sy > used) {
677fcae69d1Snicm 		lcrow = TAILQ_LAST(&lc->cells, layout_cells);
67807b91187Snicm 		layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
67907b91187Snicm 		    w->sy - used);
680fcae69d1Snicm 	}
681fcae69d1Snicm 
682fcae69d1Snicm 	/* Fix cell offsets. */
68342fbd26aSnicm 	layout_fix_offsets(w);
684baddd6b2Snicm 	layout_fix_panes(w, NULL);
685fcae69d1Snicm 
686fcae69d1Snicm 	layout_print_cell(w->layout_root, __func__, 1);
687fcae69d1Snicm 
6884a8b0ea5Snicm 	window_resize(w, lc->sx, lc->sy, -1, -1);
6894d154873Snicm 	notify_window("window-layout-changed", w);
690fcae69d1Snicm 	server_redraw_window(w);
691fcae69d1Snicm }
692