xref: /netbsd-src/external/bsd/tmux/dist/layout-set.c (revision 404ee5b9334f618040b6cdef96a0ff35a6fc4636)
1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <string.h>
22 
23 #include "tmux.h"
24 
25 /*
26  * Set window layouts - predefined methods to arrange windows. These are
27  * one-off and generate a layout tree.
28  */
29 
30 static void	layout_set_even_h(struct window *);
31 static void	layout_set_even_v(struct window *);
32 static void	layout_set_main_h(struct window *);
33 static void	layout_set_main_v(struct window *);
34 static void	layout_set_tiled(struct window *);
35 
36 static const struct {
37 	const char	*name;
38 	void	      	(*arrange)(struct window *);
39 } layout_sets[] = {
40 	{ "even-horizontal", layout_set_even_h },
41 	{ "even-vertical", layout_set_even_v },
42 	{ "main-horizontal", layout_set_main_h },
43 	{ "main-vertical", layout_set_main_v },
44 	{ "tiled", layout_set_tiled },
45 };
46 
47 int
48 layout_set_lookup(const char *name)
49 {
50 	u_int	i;
51 	int	matched = -1;
52 
53 	for (i = 0; i < nitems(layout_sets); i++) {
54 		if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
55 			if (matched != -1)	/* ambiguous */
56 				return (-1);
57 			matched = i;
58 		}
59 	}
60 
61 	return (matched);
62 }
63 
64 u_int
65 layout_set_select(struct window *w, u_int layout)
66 {
67 	if (layout > nitems(layout_sets) - 1)
68 		layout = nitems(layout_sets) - 1;
69 
70 	if (layout_sets[layout].arrange != NULL)
71 		layout_sets[layout].arrange(w);
72 
73 	w->lastlayout = layout;
74 	return (layout);
75 }
76 
77 u_int
78 layout_set_next(struct window *w)
79 {
80 	u_int	layout;
81 
82 	if (w->lastlayout == -1)
83 		layout = 0;
84 	else {
85 		layout = w->lastlayout + 1;
86 		if (layout > nitems(layout_sets) - 1)
87 			layout = 0;
88 	}
89 
90 	if (layout_sets[layout].arrange != NULL)
91 		layout_sets[layout].arrange(w);
92 	w->lastlayout = layout;
93 	return (layout);
94 }
95 
96 u_int
97 layout_set_previous(struct window *w)
98 {
99 	u_int	layout;
100 
101 	if (w->lastlayout == -1)
102 		layout = nitems(layout_sets) - 1;
103 	else {
104 		layout = w->lastlayout;
105 		if (layout == 0)
106 			layout = nitems(layout_sets) - 1;
107 		else
108 			layout--;
109 	}
110 
111 	if (layout_sets[layout].arrange != NULL)
112 		layout_sets[layout].arrange(w);
113 	w->lastlayout = layout;
114 	return (layout);
115 }
116 
117 static void
118 layout_set_even(struct window *w, enum layout_type type)
119 {
120 	struct window_pane	*wp;
121 	struct layout_cell	*lc, *lcnew;
122 	u_int			 n, sx, sy;
123 
124 	layout_print_cell(w->layout_root, __func__, 1);
125 
126 	/* Get number of panes. */
127 	n = window_count_panes(w);
128 	if (n <= 1)
129 		return;
130 
131 	/* Free the old root and construct a new. */
132 	layout_free(w);
133 	lc = w->layout_root = layout_create_cell(NULL);
134 	if (type == LAYOUT_LEFTRIGHT) {
135 		sx = (n * (PANE_MINIMUM + 1)) - 1;
136 		if (sx < w->sx)
137 			sx = w->sx;
138 		sy = w->sy;
139 	} else {
140 		sy = (n * (PANE_MINIMUM + 1)) - 1;
141 		if (sy < w->sy)
142 			sy = w->sy;
143 		sx = w->sx;
144 	}
145 	layout_set_size(lc, sx, sy, 0, 0);
146 	layout_make_node(lc, type);
147 
148 	/* Build new leaf cells. */
149 	TAILQ_FOREACH(wp, &w->panes, entry) {
150 		lcnew = layout_create_cell(lc);
151 		layout_make_leaf(lcnew, wp);
152 		lcnew->sx = w->sx;
153 		lcnew->sy = w->sy;
154 		TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
155 	}
156 
157 	/* Spread out cells. */
158 	layout_spread_cell(w, lc);
159 
160 	/* Fix cell offsets. */
161 	layout_fix_offsets(lc);
162 	layout_fix_panes(w);
163 
164 	layout_print_cell(w->layout_root, __func__, 1);
165 
166 	window_resize(w, lc->sx, lc->sy);
167 	notify_window("window-layout-changed", w);
168 	server_redraw_window(w);
169 }
170 
171 static void
172 layout_set_even_h(struct window *w)
173 {
174 	layout_set_even(w, LAYOUT_LEFTRIGHT);
175 }
176 
177 static void
178 layout_set_even_v(struct window *w)
179 {
180 	layout_set_even(w, LAYOUT_TOPBOTTOM);
181 }
182 
183 static void
184 layout_set_main_h(struct window *w)
185 {
186 	struct window_pane	*wp;
187 	struct layout_cell	*lc, *lcmain, *lcother, *lcchild;
188 	u_int			 n, mainh, otherh, sx;
189 
190 	layout_print_cell(w->layout_root, __func__, 1);
191 
192 	/* Get number of panes. */
193 	n = window_count_panes(w);
194 	if (n <= 1)
195 		return;
196 	n--;	/* take off main pane */
197 
198 	/* Get the main pane height and work out the other pane height. */
199 	mainh = options_get_number(w->options, "main-pane-height");
200 	if (mainh + PANE_MINIMUM + 1 >= w->sy) {
201 		if (w->sy <= PANE_MINIMUM + 1 + PANE_MINIMUM)
202 			mainh = PANE_MINIMUM;
203 		else
204 			mainh = w->sy - (PANE_MINIMUM + 1);
205 		otherh = PANE_MINIMUM;
206 	} else {
207 		otherh = options_get_number(w->options, "other-pane-height");
208 		if (otherh == 0)
209 			otherh = w->sy - mainh;
210 		else if (otherh > w->sy || w->sy - otherh < mainh)
211 			otherh = w->sy - mainh;
212 		else
213 			mainh = w->sy - otherh;
214 	}
215 
216 	/* Work out what width is needed. */
217 	sx = (n * (PANE_MINIMUM + 1)) - 1;
218 	if (sx < w->sx)
219 		sx = w->sx;
220 
221 	/* Free old tree and create a new root. */
222 	layout_free(w);
223 	lc = w->layout_root = layout_create_cell(NULL);
224 	layout_set_size(lc, sx, mainh + otherh, 0, 0);
225 	layout_make_node(lc, LAYOUT_TOPBOTTOM);
226 
227 	/* Create the main pane. */
228 	lcmain = layout_create_cell(lc);
229 	layout_set_size(lcmain, sx, mainh, 0, 0);
230 	layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
231 	TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
232 
233 	/* Create the other pane. */
234 	lcother = layout_create_cell(lc);
235 	layout_set_size(lcother, sx, otherh, 0, 0);
236 	if (n == 1) {
237 		wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
238 		layout_make_leaf(lcother, wp);
239 		TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
240 	} else {
241 		layout_make_node(lcother, LAYOUT_LEFTRIGHT);
242 		TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
243 
244 		/* Add the remaining panes as children. */
245 		TAILQ_FOREACH(wp, &w->panes, entry) {
246 			if (wp == TAILQ_FIRST(&w->panes))
247 				continue;
248 			lcchild = layout_create_cell(lcother);
249 			layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
250 			layout_make_leaf(lcchild, wp);
251 			TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
252 		}
253 		layout_spread_cell(w, lcother);
254 	}
255 
256 	/* Fix cell offsets. */
257 	layout_fix_offsets(lc);
258 	layout_fix_panes(w);
259 
260 	layout_print_cell(w->layout_root, __func__, 1);
261 
262 	window_resize(w, lc->sx, lc->sy);
263 	notify_window("window-layout-changed", w);
264 	server_redraw_window(w);
265 }
266 
267 static void
268 layout_set_main_v(struct window *w)
269 {
270 	struct window_pane	*wp;
271 	struct layout_cell	*lc, *lcmain, *lcother, *lcchild;
272 	u_int			 n, mainw, otherw, sy;
273 
274 	layout_print_cell(w->layout_root, __func__, 1);
275 
276 	/* Get number of panes. */
277 	n = window_count_panes(w);
278 	if (n <= 1)
279 		return;
280 	n--;	/* take off main pane */
281 
282 	/* Get the main pane width and work out the other pane width. */
283 	mainw = options_get_number(w->options, "main-pane-width");
284 	if (mainw + PANE_MINIMUM + 1 >= w->sx) {
285 		if (w->sx <= PANE_MINIMUM + 1 + PANE_MINIMUM)
286 			mainw = PANE_MINIMUM;
287 		else
288 			mainw = w->sx - (PANE_MINIMUM + 1);
289 		otherw = PANE_MINIMUM;
290 	} else {
291 		otherw = options_get_number(w->options, "other-pane-width");
292 		if (otherw == 0)
293 			otherw = w->sx - mainw;
294 		else if (otherw > w->sx || w->sx - otherw < mainw)
295 			otherw = w->sx - mainw;
296 		else
297 			mainw = w->sx - otherw;
298 	}
299 
300 	/* Work out what height is needed. */
301 	sy = (n * (PANE_MINIMUM + 1)) - 1;
302 	if (sy < w->sy)
303 		sy = w->sy;
304 
305 	/* Free old tree and create a new root. */
306 	layout_free(w);
307 	lc = w->layout_root = layout_create_cell(NULL);
308 	layout_set_size(lc, mainw + otherw, sy, 0, 0);
309 	layout_make_node(lc, LAYOUT_LEFTRIGHT);
310 
311 	/* Create the main pane. */
312 	lcmain = layout_create_cell(lc);
313 	layout_set_size(lcmain, mainw, sy, 0, 0);
314 	layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
315 	TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
316 
317 	/* Create the other pane. */
318 	lcother = layout_create_cell(lc);
319 	layout_set_size(lcother, otherw, sy, 0, 0);
320 	if (n == 1) {
321 		wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
322 		layout_make_leaf(lcother, wp);
323 		TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
324 	} else {
325 		layout_make_node(lcother, LAYOUT_TOPBOTTOM);
326 		TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
327 
328 		/* Add the remaining panes as children. */
329 		TAILQ_FOREACH(wp, &w->panes, entry) {
330 			if (wp == TAILQ_FIRST(&w->panes))
331 				continue;
332 			lcchild = layout_create_cell(lcother);
333 			layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
334 			layout_make_leaf(lcchild, wp);
335 			TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
336 		}
337 		layout_spread_cell(w, lcother);
338 	}
339 
340 	/* Fix cell offsets. */
341 	layout_fix_offsets(lc);
342 	layout_fix_panes(w);
343 
344 	layout_print_cell(w->layout_root, __func__, 1);
345 
346 	window_resize(w, lc->sx, lc->sy);
347 	notify_window("window-layout-changed", w);
348 	server_redraw_window(w);
349 }
350 
351 void
352 layout_set_tiled(struct window *w)
353 {
354 	struct window_pane	*wp;
355 	struct layout_cell	*lc, *lcrow, *lcchild;
356 	u_int			 n, width, height, used, sx, sy;
357 	u_int			 i, j, columns, rows;
358 
359 	layout_print_cell(w->layout_root, __func__, 1);
360 
361 	/* Get number of panes. */
362 	n = window_count_panes(w);
363 	if (n <= 1)
364 		return;
365 
366 	/* How many rows and columns are wanted? */
367 	rows = columns = 1;
368 	while (rows * columns < n) {
369 		rows++;
370 		if (rows * columns < n)
371 			columns++;
372 	}
373 
374 	/* What width and height should they be? */
375 	width = (w->sx - (columns - 1)) / columns;
376 	if (width < PANE_MINIMUM)
377 		width = PANE_MINIMUM;
378 	height = (w->sy - (rows - 1)) / rows;
379 	if (height < PANE_MINIMUM)
380 		height = PANE_MINIMUM;
381 
382 	/* Free old tree and create a new root. */
383 	layout_free(w);
384 	lc = w->layout_root = layout_create_cell(NULL);
385 	sx = ((width + 1) * columns) - 1;
386 	if (sx < w->sx)
387 		sx = w->sx;
388 	sy = ((height + 1) * rows) - 1;
389 	if (sy < w->sy)
390 		sy = w->sy;
391 	layout_set_size(lc, sx, sy, 0, 0);
392 	layout_make_node(lc, LAYOUT_TOPBOTTOM);
393 
394 	/* Create a grid of the cells. */
395 	wp = TAILQ_FIRST(&w->panes);
396 	for (j = 0; j < rows; j++) {
397 		/* If this is the last cell, all done. */
398 		if (wp == NULL)
399 			break;
400 
401 		/* Create the new row. */
402 		lcrow = layout_create_cell(lc);
403 		layout_set_size(lcrow, w->sx, height, 0, 0);
404 		TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
405 
406 		/* If only one column, just use the row directly. */
407 		if (n - (j * columns) == 1 || columns == 1) {
408 			layout_make_leaf(lcrow, wp);
409 			wp = TAILQ_NEXT(wp, entry);
410 			continue;
411 		}
412 
413 		/* Add in the columns. */
414 		layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
415 		for (i = 0; i < columns; i++) {
416 			/* Create and add a pane cell. */
417 			lcchild = layout_create_cell(lcrow);
418 			layout_set_size(lcchild, width, height, 0, 0);
419 			layout_make_leaf(lcchild, wp);
420 			TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
421 
422 			/* Move to the next cell. */
423 			if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
424 				break;
425 		}
426 
427 		/*
428 		 * Adjust the row and columns to fit the full width if
429 		 * necessary.
430 		 */
431 		if (i == columns)
432 			i--;
433 		used = ((i + 1) * (width + 1)) - 1;
434 		if (w->sx <= used)
435 			continue;
436 		lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
437 		layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT,
438 		    w->sx - used);
439 	}
440 
441 	/* Adjust the last row height to fit if necessary. */
442 	used = (rows * height) + rows - 1;
443 	if (w->sy > used) {
444 		lcrow = TAILQ_LAST(&lc->cells, layout_cells);
445 		layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
446 		    w->sy - used);
447 	}
448 
449 	/* Fix cell offsets. */
450 	layout_fix_offsets(lc);
451 	layout_fix_panes(w);
452 
453 	layout_print_cell(w->layout_root, __func__, 1);
454 
455 	window_resize(w, lc->sx, lc->sy);
456 	notify_window("window-layout-changed", w);
457 	server_redraw_window(w);
458 }
459