xref: /openbsd-src/usr.bin/tmux/layout-set.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /* $OpenBSD: layout-set.c,v 1.3 2009/07/28 06:48:44 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 
35 const struct {
36 	const char	*name;
37 	void	      	(*arrange)(struct window *);
38 } layout_sets[] = {
39 	{ "even-horizontal", layout_set_even_h },
40 	{ "even-vertical", layout_set_even_v },
41 	{ "main-horizontal", layout_set_main_h },
42 	{ "main-vertical", layout_set_main_v },
43 };
44 
45 const char *
46 layout_set_name(u_int layout)
47 {
48 	return (layout_sets[layout].name);
49 }
50 
51 int
52 layout_set_lookup(const char *name)
53 {
54 	u_int	i;
55 	int	matched = -1;
56 
57 	for (i = 0; i < nitems(layout_sets); i++) {
58 		if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
59 			if (matched != -1)	/* ambiguous */
60 				return (-1);
61 			matched = i;
62 		}
63 	}
64 
65 	return (matched);
66 }
67 
68 u_int
69 layout_set_select(struct window *w, u_int layout)
70 {
71 	if (layout > nitems(layout_sets) - 1)
72 		layout = nitems(layout_sets) - 1;
73 
74 	if (layout_sets[layout].arrange != NULL)
75 		layout_sets[layout].arrange(w);
76 
77 	w->lastlayout = layout;
78 	return (layout);
79 }
80 
81 u_int
82 layout_set_next(struct window *w)
83 {
84 	u_int	layout;
85 
86 	if (w->lastlayout == -1)
87 		layout = 0;
88 	else {
89 		layout = w->lastlayout + 1;
90 		if (layout > nitems(layout_sets) - 1)
91 			layout = 0;
92 	}
93 
94 	if (layout_sets[layout].arrange != NULL)
95 		layout_sets[layout].arrange(w);
96 	w->lastlayout = layout;
97 	return (layout);
98 }
99 
100 u_int
101 layout_set_previous(struct window *w)
102 {
103 	u_int	layout;
104 
105 	if (w->lastlayout == -1)
106 		layout = nitems(layout_sets) - 1;
107 	else {
108 		layout = w->lastlayout;
109 		if (layout == 0)
110 			layout = nitems(layout_sets) - 1;
111 		else
112 			layout--;
113 	}
114 
115 	if (layout_sets[layout].arrange != NULL)
116 		layout_sets[layout].arrange(w);
117 	w->lastlayout = layout;
118 	return (layout);
119 }
120 
121 void
122 layout_set_even_h(struct window *w)
123 {
124 	struct window_pane	*wp;
125 	struct layout_cell	*lc, *lcnew;
126 	u_int			 i, n, width, xoff;
127 
128 	layout_print_cell(w->layout_root, __func__, 1);
129 
130 	/* Get number of panes. */
131 	n = window_count_panes(w);
132 	if (n <= 1)
133 		return;
134 
135 	/* How many can we fit? */
136 	if (w->sx / n < PANE_MINIMUM + 1)
137 		width = PANE_MINIMUM + 1;
138 	else
139 		width = w->sx / n;
140 
141 	/* Free the old root and construct a new. */
142 	layout_free(w);
143 	lc = w->layout_root = layout_create_cell(NULL);
144 	layout_set_size(lc, w->sx, w->sy, 0, 0);
145 	layout_make_node(lc, LAYOUT_LEFTRIGHT);
146 
147 	/* Build new leaf cells. */
148 	i = xoff = 0;
149 	TAILQ_FOREACH(wp, &w->panes, entry) {
150 		/* Create child cell. */
151 		lcnew = layout_create_cell(lc);
152 		layout_set_size(lcnew, width - 1, w->sy, xoff, 0);
153 		layout_make_leaf(lcnew, wp);
154 		TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
155 
156 		i++;
157 		xoff += width;
158 	}
159 
160 	/* Allocate any remaining space. */
161 	if (w->sx > xoff - 1) {
162 		lc = TAILQ_LAST(&lc->cells, layout_cells);
163 		layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, w->sx - (xoff - 1));
164 	}
165 
166 	/* Fix cell offsets. */
167 	layout_fix_offsets(lc);
168 	layout_fix_panes(w, w->sx, w->sy);
169 
170 	layout_print_cell(w->layout_root, __func__, 1);
171 
172 	server_redraw_window(w);
173 }
174 
175 void
176 layout_set_even_v(struct window *w)
177 {
178 	struct window_pane	*wp;
179 	struct layout_cell	*lc, *lcnew;
180 	u_int			 i, n, height, yoff;
181 
182 	layout_print_cell(w->layout_root, __func__, 1);
183 
184 	/* Get number of panes. */
185 	n = window_count_panes(w);
186 	if (n <= 1)
187 		return;
188 
189 	/* How many can we fit? */
190 	if (w->sy / n < PANE_MINIMUM + 1)
191 		height = PANE_MINIMUM + 1;
192 	else
193 		height = w->sy / n;
194 
195 	/* Free the old root and construct a new. */
196 	layout_free(w);
197 	lc = w->layout_root = layout_create_cell(NULL);
198 	layout_set_size(lc, w->sx, w->sy, 0, 0);
199 	layout_make_node(lc, LAYOUT_TOPBOTTOM);
200 
201 	/* Build new leaf cells. */
202 	i = yoff = 0;
203 	TAILQ_FOREACH(wp, &w->panes, entry) {
204 		/* Create child cell. */
205 		lcnew = layout_create_cell(lc);
206 		layout_set_size(lcnew, w->sx, height - 1, 0, yoff);
207 		layout_make_leaf(lcnew, wp);
208 		TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
209 
210 		i++;
211 		yoff += height;
212 	}
213 
214 	/* Allocate any remaining space. */
215 	if (w->sy > yoff - 1) {
216 		lc = TAILQ_LAST(&lc->cells, layout_cells);
217 		layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, w->sy - (yoff - 1));
218 	}
219 
220 	/* Fix cell offsets. */
221 	layout_fix_offsets(lc);
222 	layout_fix_panes(w, w->sx, w->sy);
223 
224 	layout_print_cell(w->layout_root, __func__, 1);
225 
226 	server_redraw_window(w);
227 }
228 
229 void
230 layout_set_main_h(struct window *w)
231 {
232 	struct window_pane	*wp;
233 	struct layout_cell	*lc, *lcmain, *lcrow, *lcchild;
234 	u_int			 n, mainheight, width, height, used;
235 	u_int			 i, j, columns, rows, totalrows;
236 
237 	layout_print_cell(w->layout_root, __func__, 1);
238 
239 	/* Get number of panes. */
240 	n = window_count_panes(w);
241 	if (n <= 1)
242 		return;
243 	n--;	/* take off main pane */
244 
245 	/* How many rows and columns will be needed? */
246 	columns = w->sx / (PANE_MINIMUM + 1);	/* maximum columns */
247 	rows = 1 + (n - 1) / columns;
248 	columns = 1 + (n - 1) / rows;
249 	width = w->sx / columns;
250 
251 	/* Get the main pane height and add one for separator line. */
252 	mainheight = options_get_number(&w->options, "main-pane-height") + 1;
253 	if (mainheight < PANE_MINIMUM + 1)
254 		mainheight = PANE_MINIMUM + 1;
255 
256 	/* Try and make everything fit. */
257 	totalrows = rows * (PANE_MINIMUM + 1) - 1;
258 	if (mainheight + totalrows > w->sy) {
259 		if (totalrows + PANE_MINIMUM + 1 > w->sy)
260 			mainheight = PANE_MINIMUM + 2;
261 		else
262 			mainheight = w->sy - totalrows;
263 		height = PANE_MINIMUM + 1;
264 	} else
265 		height = (w->sy - mainheight) / rows;
266 
267 	/* Free old tree and create a new root. */
268 	layout_free(w);
269 	lc = w->layout_root = layout_create_cell(NULL);
270 	layout_set_size(lc, w->sx, mainheight + rows * height, 0, 0);
271 	layout_make_node(lc, LAYOUT_TOPBOTTOM);
272 
273 	/* Create the main pane. */
274 	lcmain = layout_create_cell(lc);
275 	layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0);
276 	layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
277 	TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
278 
279 	/* Create a grid of the remaining cells. */
280 	wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
281 	for (j = 0; j < rows; j++) {
282 		/* If this is the last cell, all done. */
283 		if (wp == NULL)
284 			break;
285 
286 		/* Create the new row. */
287 		lcrow = layout_create_cell(lc);
288 		layout_set_size(lcrow, w->sx, height - 1, 0, 0);
289 		TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
290 
291 		/* If only one column, just use the row directly. */
292 		if (columns == 1) {
293 			layout_make_leaf(lcrow, wp);
294 			wp = TAILQ_NEXT(wp, entry);
295 			continue;
296 		}
297 
298  		/* Add in the columns. */
299 		layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
300 		for (i = 0; i < columns; i++) {
301 			/* Create and add a pane cell. */
302 			lcchild = layout_create_cell(lcrow);
303 			layout_set_size(lcchild, width - 1, height - 1, 0, 0);
304 			layout_make_leaf(lcchild, wp);
305 			TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
306 
307 			/* Move to the next cell. */
308 			if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
309 				break;
310 		}
311 
312 		/* Adjust the row to fit the full width if necessary. */
313 		if (i == columns)
314 			i--;
315  		used = ((i + 1) * width) - 1;
316  		if (w->sx <= used)
317  			continue;
318  		lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
319  		layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used);
320 	}
321 
322 	/* Adjust the last row height to fit if necessary. */
323 	used = mainheight + (rows * height) - 1;
324 	if (w->sy > used) {
325 		lcrow = TAILQ_LAST(&lc->cells, layout_cells);
326 		layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used);
327 	}
328 
329 	/* Fix cell offsets. */
330 	layout_fix_offsets(lc);
331 	layout_fix_panes(w, w->sx, w->sy);
332 
333 	layout_print_cell(w->layout_root, __func__, 1);
334 
335 	server_redraw_window(w);
336 }
337 
338 void
339 layout_set_main_v(struct window *w)
340 {
341 	struct window_pane	*wp;
342 	struct layout_cell	*lc, *lcmain, *lccolumn, *lcchild;
343 	u_int			 n, mainwidth, width, height, used;
344 	u_int			 i, j, columns, rows, totalcolumns;
345 
346 	layout_print_cell(w->layout_root, __func__, 1);
347 
348 	/* Get number of panes. */
349 	n = window_count_panes(w);
350 	if (n <= 1)
351 		return;
352 	n--;	/* take off main pane */
353 
354 	/* How many rows and columns will be needed? */
355 	rows = w->sy / (PANE_MINIMUM + 1);	/* maximum rows */
356 	columns = 1 + (n - 1) / rows;
357 	rows = 1 + (n - 1) / columns;
358 	height = w->sy / rows;
359 
360 	/* Get the main pane width and add one for separator line. */
361 	mainwidth = options_get_number(&w->options, "main-pane-width") + 1;
362 	if (mainwidth < PANE_MINIMUM + 1)
363 		mainwidth = PANE_MINIMUM + 1;
364 
365 	/* Try and make everything fit. */
366 	totalcolumns = columns * (PANE_MINIMUM + 1) - 1;
367 	if (mainwidth + totalcolumns > w->sx) {
368 		if (totalcolumns + PANE_MINIMUM + 1 > w->sx)
369 			mainwidth = PANE_MINIMUM + 2;
370 		else
371 			mainwidth = w->sx - totalcolumns;
372 		width = PANE_MINIMUM + 1;
373 	} else
374 		width = (w->sx - mainwidth) / columns;
375 
376 	/* Free old tree and create a new root. */
377 	layout_free(w);
378 	lc = w->layout_root = layout_create_cell(NULL);
379 	layout_set_size(lc, mainwidth + columns * width, w->sy, 0, 0);
380 	layout_make_node(lc, LAYOUT_LEFTRIGHT);
381 
382 	/* Create the main pane. */
383 	lcmain = layout_create_cell(lc);
384 	layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0);
385 	layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
386 	TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
387 
388 	/* Create a grid of the remaining cells. */
389 	wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
390 	for (j = 0; j < columns; j++) {
391 		/* If this is the last cell, all done. */
392 		if (wp == NULL)
393 			break;
394 
395 		/* Create the new column. */
396 		lccolumn = layout_create_cell(lc);
397 		layout_set_size(lccolumn, width - 1, w->sy, 0, 0);
398 		TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry);
399 
400 		/* If only one row, just use the row directly. */
401 		if (rows == 1) {
402 			layout_make_leaf(lccolumn, wp);
403 			wp = TAILQ_NEXT(wp, entry);
404 			continue;
405 		}
406 
407 		/* Add in the rows. */
408 		layout_make_node(lccolumn, LAYOUT_TOPBOTTOM);
409 		for (i = 0; i < rows; i++) {
410 			/* Create and add a pane cell. */
411 			lcchild = layout_create_cell(lccolumn);
412 			layout_set_size(lcchild, width - 1, height - 1, 0, 0);
413 			layout_make_leaf(lcchild, wp);
414 			TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry);
415 
416 			/* Move to the next cell. */
417 			if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
418 				break;
419 		}
420 
421 		/* Adjust the column to fit the full height if necessary. */
422 		if (i == rows)
423 			i--;
424 		used = ((i + 1) * height) - 1;
425  		if (w->sy <= used)
426  			continue;
427  		lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells);
428  		layout_resize_adjust(lcchild, LAYOUT_TOPBOTTOM, w->sy - used);
429 	}
430 
431 	/* Adjust the last column width to fit if necessary. */
432 	used = mainwidth + (columns * width) - 1;
433 	if (w->sx > used) {
434 		lccolumn = TAILQ_LAST(&lc->cells, layout_cells);
435 		layout_resize_adjust(lccolumn, LAYOUT_LEFTRIGHT, w->sx - used);
436 	}
437 
438 	/* Fix cell offsets. */
439 	layout_fix_offsets(lc);
440 	layout_fix_panes(w, w->sx, w->sy);
441 
442 	layout_print_cell(w->layout_root, __func__, 1);
443 
444 	server_redraw_window(w);
445 }
446