Use server_destroy_session() for kill-session.
[tmux-openbsd.git] / layout-set.c
blob7ebdead948969ce002fd09024d6e434c51f914c1
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
21 #include <string.h>
23 #include "tmux.h"
26 * Set window layouts - predefined methods to arrange windows. These are one-off
27 * and generate a layout tree.
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 *);
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 },
47 const char *
48 layout_set_name(u_int layout)
50 return (layout_sets[layout].name);
53 int
54 layout_set_lookup(const char *name)
56 u_int i;
57 int matched = -1;
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;
67 return (matched);
70 u_int
71 layout_set_select(struct window *w, u_int layout)
73 if (layout > nitems(layout_sets) - 1)
74 layout = nitems(layout_sets) - 1;
76 if (layout_sets[layout].arrange != NULL)
77 layout_sets[layout].arrange(w);
79 w->lastlayout = layout;
80 return (layout);
83 u_int
84 layout_set_next(struct window *w)
86 u_int layout;
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;
96 if (layout_sets[layout].arrange != NULL)
97 layout_sets[layout].arrange(w);
98 w->lastlayout = layout;
99 return (layout);
102 u_int
103 layout_set_previous(struct window *w)
105 u_int layout;
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--;
117 if (layout_sets[layout].arrange != NULL)
118 layout_sets[layout].arrange(w);
119 w->lastlayout = layout;
120 return (layout);
123 void
124 layout_set_even_h(struct window *w)
126 struct window_pane *wp;
127 struct layout_cell *lc, *lcnew;
128 u_int i, n, width, xoff;
130 layout_print_cell(w->layout_root, __func__, 1);
132 /* Get number of panes. */
133 n = window_count_panes(w);
134 if (n <= 1)
135 return;
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;
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);
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);
158 i++;
159 xoff += width;
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));
168 /* Fix cell offsets. */
169 layout_fix_offsets(lc);
170 layout_fix_panes(w, w->sx, w->sy);
172 layout_print_cell(w->layout_root, __func__, 1);
174 server_redraw_window(w);
177 void
178 layout_set_even_v(struct window *w)
180 struct window_pane *wp;
181 struct layout_cell *lc, *lcnew;
182 u_int i, n, height, yoff;
184 layout_print_cell(w->layout_root, __func__, 1);
186 /* Get number of panes. */
187 n = window_count_panes(w);
188 if (n <= 1)
189 return;
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;
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);
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);
212 i++;
213 yoff += height;
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));
222 /* Fix cell offsets. */
223 layout_fix_offsets(lc);
224 layout_fix_panes(w, w->sx, w->sy);
226 layout_print_cell(w->layout_root, __func__, 1);
228 server_redraw_window(w);
231 void
232 layout_set_main_h(struct window *w)
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;
239 layout_print_cell(w->layout_root, __func__, 1);
241 /* Get number of panes. */
242 n = window_count_panes(w);
243 if (n <= 1)
244 return;
245 n--; /* take off main pane */
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;
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;
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;
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);
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);
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;
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);
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;
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);
311 /* Move to the next cell. */
312 if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
313 break;
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);
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);
333 /* Fix cell offsets. */
334 layout_fix_offsets(lc);
335 layout_fix_panes(w, w->sx, w->sy);
337 layout_print_cell(w->layout_root, __func__, 1);
339 server_redraw_window(w);
342 void
343 layout_set_main_v(struct window *w)
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;
350 layout_print_cell(w->layout_root, __func__, 1);
352 /* Get number of panes. */
353 n = window_count_panes(w);
354 if (n <= 1)
355 return;
356 n--; /* take off main pane */
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;
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;
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;
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);
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);
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;
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);
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;
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);
422 /* Move to the next cell. */
423 if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
424 break;
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);
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);
444 /* Fix cell offsets. */
445 layout_fix_offsets(lc);
446 layout_fix_panes(w, w->sx, w->sy);
448 layout_print_cell(w->layout_root, __func__, 1);
450 server_redraw_window(w);
453 void
454 layout_set_tiled(struct window *w)
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;
461 layout_print_cell(w->layout_root, __func__, 1);
463 /* Get number of panes. */
464 n = window_count_panes(w);
465 if (n <= 1)
466 return;
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++;
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;
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);
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;
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);
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;
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);
518 /* Move to the next cell. */
519 if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
520 break;
524 * Adjust the row and columns to fit the full width if
525 * necessary.
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);
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);
543 /* Fix cell offsets. */
544 layout_fix_offsets(lc);
545 layout_fix_panes(w, w->sx, w->sy);
547 layout_print_cell(w->layout_root, __func__, 1);
549 server_redraw_window(w);