xref: /openbsd-src/usr.bin/tmux/layout-set.c (revision cd1eb269cafb12c415be1749cd4a4b5422710415)
1 /* $OpenBSD: layout-set.c,v 1.6 2010/04/25 20:28:13 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
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 one-off
27  * and generate a layout tree.
28  */
29 
30 void	layout_set_even_h(struct window *);
31 void	layout_set_even_v(struct window *);
32 void	layout_set_main_h(struct window *);
33 void	layout_set_main_v(struct window *);
34 void	layout_set_tiled(struct window *);
35 
36 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 const char *
48 layout_set_name(u_int layout)
49 {
50 	return (layout_sets[layout].name);
51 }
52 
53 int
54 layout_set_lookup(const char *name)
55 {
56 	u_int	i;
57 	int	matched = -1;
58 
59 	for (i = 0; i < nitems(layout_sets); i++) {
60 		if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
61 			if (matched != -1)	/* ambiguous */
62 				return (-1);
63 			matched = i;
64 		}
65 	}
66 
67 	return (matched);
68 }
69 
70 u_int
71 layout_set_select(struct window *w, u_int layout)
72 {
73 	if (layout > nitems(layout_sets) - 1)
74 		layout = nitems(layout_sets) - 1;
75 
76 	if (layout_sets[layout].arrange != NULL)
77 		layout_sets[layout].arrange(w);
78 
79 	w->lastlayout = layout;
80 	return (layout);
81 }
82 
83 u_int
84 layout_set_next(struct window *w)
85 {
86 	u_int	layout;
87 
88 	if (w->lastlayout == -1)
89 		layout = 0;
90 	else {
91 		layout = w->lastlayout + 1;
92 		if (layout > nitems(layout_sets) - 1)
93 			layout = 0;
94 	}
95 
96 	if (layout_sets[layout].arrange != NULL)
97 		layout_sets[layout].arrange(w);
98 	w->lastlayout = layout;
99 	return (layout);
100 }
101 
102 u_int
103 layout_set_previous(struct window *w)
104 {
105 	u_int	layout;
106 
107 	if (w->lastlayout == -1)
108 		layout = nitems(layout_sets) - 1;
109 	else {
110 		layout = w->lastlayout;
111 		if (layout == 0)
112 			layout = nitems(layout_sets) - 1;
113 		else
114 			layout--;
115 	}
116 
117 	if (layout_sets[layout].arrange != NULL)
118 		layout_sets[layout].arrange(w);
119 	w->lastlayout = layout;
120 	return (layout);
121 }
122 
123 void
124 layout_set_even_h(struct window *w)
125 {
126 	struct window_pane	*wp;
127 	struct layout_cell	*lc, *lcnew;
128 	u_int			 i, n, width, xoff;
129 
130 	layout_print_cell(w->layout_root, __func__, 1);
131 
132 	/* Get number of panes. */
133 	n = window_count_panes(w);
134 	if (n <= 1)
135 		return;
136 
137 	/* How many can we fit? */
138 	if (w->sx / n < PANE_MINIMUM + 1)
139 		width = PANE_MINIMUM + 1;
140 	else
141 		width = w->sx / n;
142 
143 	/* Free the old root and construct a new. */
144 	layout_free(w);
145 	lc = w->layout_root = layout_create_cell(NULL);
146 	layout_set_size(lc, w->sx, w->sy, 0, 0);
147 	layout_make_node(lc, LAYOUT_LEFTRIGHT);
148 
149 	/* Build new leaf cells. */
150 	i = xoff = 0;
151 	TAILQ_FOREACH(wp, &w->panes, entry) {
152 		/* Create child cell. */
153 		lcnew = layout_create_cell(lc);
154 		layout_set_size(lcnew, width - 1, w->sy, xoff, 0);
155 		layout_make_leaf(lcnew, wp);
156 		TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
157 
158 		i++;
159 		xoff += width;
160 	}
161 
162 	/* Allocate any remaining space. */
163 	if (w->sx > xoff - 1) {
164 		lc = TAILQ_LAST(&lc->cells, layout_cells);
165 		layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, w->sx - (xoff - 1));
166 	}
167 
168 	/* Fix cell offsets. */
169 	layout_fix_offsets(lc);
170 	layout_fix_panes(w, w->sx, w->sy);
171 
172 	layout_print_cell(w->layout_root, __func__, 1);
173 
174 	server_redraw_window(w);
175 }
176 
177 void
178 layout_set_even_v(struct window *w)
179 {
180 	struct window_pane	*wp;
181 	struct layout_cell	*lc, *lcnew;
182 	u_int			 i, n, height, yoff;
183 
184 	layout_print_cell(w->layout_root, __func__, 1);
185 
186 	/* Get number of panes. */
187 	n = window_count_panes(w);
188 	if (n <= 1)
189 		return;
190 
191 	/* How many can we fit? */
192 	if (w->sy / n < PANE_MINIMUM + 1)
193 		height = PANE_MINIMUM + 1;
194 	else
195 		height = w->sy / n;
196 
197 	/* Free the old root and construct a new. */
198 	layout_free(w);
199 	lc = w->layout_root = layout_create_cell(NULL);
200 	layout_set_size(lc, w->sx, w->sy, 0, 0);
201 	layout_make_node(lc, LAYOUT_TOPBOTTOM);
202 
203 	/* Build new leaf cells. */
204 	i = yoff = 0;
205 	TAILQ_FOREACH(wp, &w->panes, entry) {
206 		/* Create child cell. */
207 		lcnew = layout_create_cell(lc);
208 		layout_set_size(lcnew, w->sx, height - 1, 0, yoff);
209 		layout_make_leaf(lcnew, wp);
210 		TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
211 
212 		i++;
213 		yoff += height;
214 	}
215 
216 	/* Allocate any remaining space. */
217 	if (w->sy > yoff - 1) {
218 		lc = TAILQ_LAST(&lc->cells, layout_cells);
219 		layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, w->sy - (yoff - 1));
220 	}
221 
222 	/* Fix cell offsets. */
223 	layout_fix_offsets(lc);
224 	layout_fix_panes(w, w->sx, w->sy);
225 
226 	layout_print_cell(w->layout_root, __func__, 1);
227 
228 	server_redraw_window(w);
229 }
230 
231 void
232 layout_set_main_h(struct window *w)
233 {
234 	struct window_pane	*wp;
235 	struct layout_cell	*lc, *lcmain, *lcrow, *lcchild;
236 	u_int			 n, mainheight, width, height, used;
237 	u_int			 i, j, columns, rows, totalrows;
238 
239 	layout_print_cell(w->layout_root, __func__, 1);
240 
241 	/* Get number of panes. */
242 	n = window_count_panes(w);
243 	if (n <= 1)
244 		return;
245 	n--;	/* take off main pane */
246 
247 	/* How many rows and columns will be needed? */
248 	columns = w->sx / (PANE_MINIMUM + 1);	/* maximum columns */
249 	if (columns == 0)
250 		columns = 1;
251 	rows = 1 + (n - 1) / columns;
252 	columns = 1 + (n - 1) / rows;
253 	width = w->sx / columns;
254 
255 	/* Get the main pane height and add one for separator line. */
256 	mainheight = options_get_number(&w->options, "main-pane-height") + 1;
257 	if (mainheight < PANE_MINIMUM + 1)
258 		mainheight = PANE_MINIMUM + 1;
259 
260 	/* Try and make everything fit. */
261 	totalrows = rows * (PANE_MINIMUM + 1) - 1;
262 	if (mainheight + totalrows > w->sy) {
263 		if (totalrows + PANE_MINIMUM + 1 > w->sy)
264 			mainheight = PANE_MINIMUM + 2;
265 		else
266 			mainheight = w->sy - totalrows;
267 		height = PANE_MINIMUM + 1;
268 	} else
269 		height = (w->sy - mainheight) / rows;
270 
271 	/* Free old tree and create a new root. */
272 	layout_free(w);
273 	lc = w->layout_root = layout_create_cell(NULL);
274 	layout_set_size(lc, w->sx, mainheight + rows * height, 0, 0);
275 	layout_make_node(lc, LAYOUT_TOPBOTTOM);
276 
277 	/* Create the main pane. */
278 	lcmain = layout_create_cell(lc);
279 	layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0);
280 	layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
281 	TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
282 
283 	/* Create a grid of the remaining cells. */
284 	wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
285 	for (j = 0; j < rows; j++) {
286 		/* If this is the last cell, all done. */
287 		if (wp == NULL)
288 			break;
289 
290 		/* Create the new row. */
291 		lcrow = layout_create_cell(lc);
292 		layout_set_size(lcrow, w->sx, height - 1, 0, 0);
293 		TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
294 
295 		/* If only one column, just use the row directly. */
296 		if (columns == 1) {
297 			layout_make_leaf(lcrow, wp);
298 			wp = TAILQ_NEXT(wp, entry);
299 			continue;
300 		}
301 
302 		/* Add in the columns. */
303 		layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
304 		for (i = 0; i < columns; i++) {
305 			/* Create and add a pane cell. */
306 			lcchild = layout_create_cell(lcrow);
307 			layout_set_size(lcchild, width - 1, height - 1, 0, 0);
308 			layout_make_leaf(lcchild, wp);
309 			TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
310 
311 			/* Move to the next cell. */
312 			if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
313 				break;
314 		}
315 
316 		/* Adjust the row to fit the full width if necessary. */
317 		if (i == columns)
318 			i--;
319 		used = ((i + 1) * width) - 1;
320 		if (w->sx <= used)
321 			continue;
322 		lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
323 		layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used);
324 	}
325 
326 	/* Adjust the last row height to fit if necessary. */
327 	used = mainheight + (rows * height) - 1;
328 	if (w->sy > used) {
329 		lcrow = TAILQ_LAST(&lc->cells, layout_cells);
330 		layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used);
331 	}
332 
333 	/* Fix cell offsets. */
334 	layout_fix_offsets(lc);
335 	layout_fix_panes(w, w->sx, w->sy);
336 
337 	layout_print_cell(w->layout_root, __func__, 1);
338 
339 	server_redraw_window(w);
340 }
341 
342 void
343 layout_set_main_v(struct window *w)
344 {
345 	struct window_pane	*wp;
346 	struct layout_cell	*lc, *lcmain, *lccolumn, *lcchild;
347 	u_int			 n, mainwidth, width, height, used;
348 	u_int			 i, j, columns, rows, totalcolumns;
349 
350 	layout_print_cell(w->layout_root, __func__, 1);
351 
352 	/* Get number of panes. */
353 	n = window_count_panes(w);
354 	if (n <= 1)
355 		return;
356 	n--;	/* take off main pane */
357 
358 	/* How many rows and columns will be needed? */
359 	rows = w->sy / (PANE_MINIMUM + 1);	/* maximum rows */
360 	if (rows == 0)
361 		rows = 1;
362 	columns = 1 + (n - 1) / rows;
363 	rows = 1 + (n - 1) / columns;
364 	height = w->sy / rows;
365 
366 	/* Get the main pane width and add one for separator line. */
367 	mainwidth = options_get_number(&w->options, "main-pane-width") + 1;
368 	if (mainwidth < PANE_MINIMUM + 1)
369 		mainwidth = PANE_MINIMUM + 1;
370 
371 	/* Try and make everything fit. */
372 	totalcolumns = columns * (PANE_MINIMUM + 1) - 1;
373 	if (mainwidth + totalcolumns > w->sx) {
374 		if (totalcolumns + PANE_MINIMUM + 1 > w->sx)
375 			mainwidth = PANE_MINIMUM + 2;
376 		else
377 			mainwidth = w->sx - totalcolumns;
378 		width = PANE_MINIMUM + 1;
379 	} else
380 		width = (w->sx - mainwidth) / columns;
381 
382 	/* Free old tree and create a new root. */
383 	layout_free(w);
384 	lc = w->layout_root = layout_create_cell(NULL);
385 	layout_set_size(lc, mainwidth + columns * width, w->sy, 0, 0);
386 	layout_make_node(lc, LAYOUT_LEFTRIGHT);
387 
388 	/* Create the main pane. */
389 	lcmain = layout_create_cell(lc);
390 	layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0);
391 	layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
392 	TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
393 
394 	/* Create a grid of the remaining cells. */
395 	wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
396 	for (j = 0; j < columns; j++) {
397 		/* If this is the last cell, all done. */
398 		if (wp == NULL)
399 			break;
400 
401 		/* Create the new column. */
402 		lccolumn = layout_create_cell(lc);
403 		layout_set_size(lccolumn, width - 1, w->sy, 0, 0);
404 		TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry);
405 
406 		/* If only one row, just use the row directly. */
407 		if (rows == 1) {
408 			layout_make_leaf(lccolumn, wp);
409 			wp = TAILQ_NEXT(wp, entry);
410 			continue;
411 		}
412 
413 		/* Add in the rows. */
414 		layout_make_node(lccolumn, LAYOUT_TOPBOTTOM);
415 		for (i = 0; i < rows; i++) {
416 			/* Create and add a pane cell. */
417 			lcchild = layout_create_cell(lccolumn);
418 			layout_set_size(lcchild, width - 1, height - 1, 0, 0);
419 			layout_make_leaf(lcchild, wp);
420 			TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry);
421 
422 			/* Move to the next cell. */
423 			if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
424 				break;
425 		}
426 
427 		/* Adjust the column to fit the full height if necessary. */
428 		if (i == rows)
429 			i--;
430 		used = ((i + 1) * height) - 1;
431 		if (w->sy <= used)
432 			continue;
433 		lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells);
434 		layout_resize_adjust(lcchild, LAYOUT_TOPBOTTOM, w->sy - used);
435 	}
436 
437 	/* Adjust the last column width to fit if necessary. */
438 	used = mainwidth + (columns * width) - 1;
439 	if (w->sx > used) {
440 		lccolumn = TAILQ_LAST(&lc->cells, layout_cells);
441 		layout_resize_adjust(lccolumn, LAYOUT_LEFTRIGHT, w->sx - used);
442 	}
443 
444 	/* Fix cell offsets. */
445 	layout_fix_offsets(lc);
446 	layout_fix_panes(w, w->sx, w->sy);
447 
448 	layout_print_cell(w->layout_root, __func__, 1);
449 
450 	server_redraw_window(w);
451 }
452 
453 void
454 layout_set_tiled(struct window *w)
455 {
456 	struct window_pane	*wp;
457 	struct layout_cell	*lc, *lcrow, *lcchild;
458 	u_int			 n, width, height, used;
459 	u_int			 i, j, columns, rows;
460 
461 	layout_print_cell(w->layout_root, __func__, 1);
462 
463 	/* Get number of panes. */
464 	n = window_count_panes(w);
465 	if (n <= 1)
466 		return;
467 
468 	/* How many rows and columns are wanted? */
469 	rows = columns = 1;
470 	while (rows * columns < n) {
471 		rows++;
472 		if (rows * columns < n)
473 			columns++;
474 	}
475 
476 	/* What width and height should they be? */
477 	width = w->sx / columns;
478 	if (width < PANE_MINIMUM + 1)
479 		width = PANE_MINIMUM + 1;
480 	height = w->sy / rows;
481 	if (width < PANE_MINIMUM + 1)
482 		width = PANE_MINIMUM + 1;
483 
484 	/* Free old tree and create a new root. */
485 	layout_free(w);
486 	lc = w->layout_root = layout_create_cell(NULL);
487 	layout_set_size(lc, width * columns, height * rows, 0, 0);
488 	layout_make_node(lc, LAYOUT_TOPBOTTOM);
489 
490 	/* Create a grid of the cells. */
491 	wp = TAILQ_FIRST(&w->panes);
492 	for (j = 0; j < rows; j++) {
493 		/* If this is the last cell, all done. */
494 		if (wp == NULL)
495 			break;
496 
497 		/* Create the new row. */
498 		lcrow = layout_create_cell(lc);
499 		layout_set_size(lcrow, w->sx, height - 1, 0, 0);
500 		TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
501 
502 		/* If only one column, just use the row directly. */
503 		if (n - (j * columns) == 1) {
504 			layout_make_leaf(lcrow, wp);
505 			wp = TAILQ_NEXT(wp, entry);
506 			continue;
507 		}
508 
509 		/* Add in the columns. */
510 		layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
511 		for (i = 0; i < columns; i++) {
512 			/* Create and add a pane cell. */
513 			lcchild = layout_create_cell(lcrow);
514 			layout_set_size(lcchild, width - 1, height - 1, 0, 0);
515 			layout_make_leaf(lcchild, wp);
516 			TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
517 
518 			/* Move to the next cell. */
519 			if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
520 				break;
521 		}
522 
523 		/*
524 		 * Adjust the row and columns to fit the full width if
525 		 * necessary.
526 		 */
527 		if (i == columns)
528 			i--;
529 		used = ((i + 1) * width) - 1;
530 		if (w->sx <= used)
531 			continue;
532 		lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
533 		layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used);
534 	}
535 
536 	/* Adjust the last row height to fit if necessary. */
537 	used = (rows * height) - 1;
538 	if (w->sy > used) {
539 		lcrow = TAILQ_LAST(&lc->cells, layout_cells);
540 		layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used);
541 	}
542 
543 	/* Fix cell offsets. */
544 	layout_fix_offsets(lc);
545 	layout_fix_panes(w, w->sx, w->sy);
546 
547 	layout_print_cell(w->layout_root, __func__, 1);
548 
549 	server_redraw_window(w);
550 }
551