Merge branch 'obsd-master'
[tmux.git] / layout-set.c
blob642dbffb911ce599f6f471050181b4419fe81039
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
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 <stdlib.h>
22 #include <string.h>
24 #include "tmux.h"
27 * Set window layouts - predefined methods to arrange windows. These are
28 * one-off and generate a layout tree.
31 static void layout_set_even_h(struct window *);
32 static void layout_set_even_v(struct window *);
33 static void layout_set_main_h(struct window *);
34 static void layout_set_main_h_mirrored(struct window *);
35 static void layout_set_main_v(struct window *);
36 static void layout_set_main_v_mirrored(struct window *);
37 static void layout_set_tiled(struct window *);
39 static const struct {
40 const char *name;
41 void (*arrange)(struct window *);
42 } layout_sets[] = {
43 { "even-horizontal", layout_set_even_h },
44 { "even-vertical", layout_set_even_v },
45 { "main-horizontal", layout_set_main_h },
46 { "main-horizontal-mirrored", layout_set_main_h_mirrored },
47 { "main-vertical", layout_set_main_v },
48 { "main-vertical-mirrored", layout_set_main_v_mirrored },
49 { "tiled", layout_set_tiled },
52 int
53 layout_set_lookup(const char *name)
55 u_int i;
56 int matched = -1;
58 for (i = 0; i < nitems(layout_sets); i++) {
59 if (strcmp(layout_sets[i].name, name) == 0)
60 return (i);
62 for (i = 0; i < nitems(layout_sets); i++) {
63 if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
64 if (matched != -1) /* ambiguous */
65 return (-1);
66 matched = i;
70 return (matched);
73 u_int
74 layout_set_select(struct window *w, u_int layout)
76 if (layout > nitems(layout_sets) - 1)
77 layout = nitems(layout_sets) - 1;
79 if (layout_sets[layout].arrange != NULL)
80 layout_sets[layout].arrange(w);
82 w->lastlayout = layout;
83 return (layout);
86 u_int
87 layout_set_next(struct window *w)
89 u_int layout;
91 if (w->lastlayout == -1)
92 layout = 0;
93 else {
94 layout = w->lastlayout + 1;
95 if (layout > nitems(layout_sets) - 1)
96 layout = 0;
99 if (layout_sets[layout].arrange != NULL)
100 layout_sets[layout].arrange(w);
101 w->lastlayout = layout;
102 return (layout);
105 u_int
106 layout_set_previous(struct window *w)
108 u_int layout;
110 if (w->lastlayout == -1)
111 layout = nitems(layout_sets) - 1;
112 else {
113 layout = w->lastlayout;
114 if (layout == 0)
115 layout = nitems(layout_sets) - 1;
116 else
117 layout--;
120 if (layout_sets[layout].arrange != NULL)
121 layout_sets[layout].arrange(w);
122 w->lastlayout = layout;
123 return (layout);
126 static void
127 layout_set_even(struct window *w, enum layout_type type)
129 struct window_pane *wp;
130 struct layout_cell *lc, *lcnew;
131 u_int n, sx, sy;
133 layout_print_cell(w->layout_root, __func__, 1);
135 /* Get number of panes. */
136 n = window_count_panes(w);
137 if (n <= 1)
138 return;
140 /* Free the old root and construct a new. */
141 layout_free(w);
142 lc = w->layout_root = layout_create_cell(NULL);
143 if (type == LAYOUT_LEFTRIGHT) {
144 sx = (n * (PANE_MINIMUM + 1)) - 1;
145 if (sx < w->sx)
146 sx = w->sx;
147 sy = w->sy;
148 } else {
149 sy = (n * (PANE_MINIMUM + 1)) - 1;
150 if (sy < w->sy)
151 sy = w->sy;
152 sx = w->sx;
154 layout_set_size(lc, sx, sy, 0, 0);
155 layout_make_node(lc, type);
157 /* Build new leaf cells. */
158 TAILQ_FOREACH(wp, &w->panes, entry) {
159 lcnew = layout_create_cell(lc);
160 layout_make_leaf(lcnew, wp);
161 lcnew->sx = w->sx;
162 lcnew->sy = w->sy;
163 TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
166 /* Spread out cells. */
167 layout_spread_cell(w, lc);
169 /* Fix cell offsets. */
170 layout_fix_offsets(w);
171 layout_fix_panes(w, NULL);
173 layout_print_cell(w->layout_root, __func__, 1);
175 window_resize(w, lc->sx, lc->sy, -1, -1);
176 notify_window("window-layout-changed", w);
177 server_redraw_window(w);
180 static void
181 layout_set_even_h(struct window *w)
183 layout_set_even(w, LAYOUT_LEFTRIGHT);
186 static void
187 layout_set_even_v(struct window *w)
189 layout_set_even(w, LAYOUT_TOPBOTTOM);
192 static void
193 layout_set_main_h(struct window *w)
195 struct window_pane *wp;
196 struct layout_cell *lc, *lcmain, *lcother, *lcchild;
197 u_int n, mainh, otherh, sx, sy;
198 char *cause;
199 const char *s;
201 layout_print_cell(w->layout_root, __func__, 1);
203 /* Get number of panes. */
204 n = window_count_panes(w);
205 if (n <= 1)
206 return;
207 n--; /* take off main pane */
209 /* Find available height - take off one line for the border. */
210 sy = w->sy - 1;
212 /* Get the main pane height. */
213 s = options_get_string(w->options, "main-pane-height");
214 mainh = args_string_percentage(s, 0, sy, sy, &cause);
215 if (cause != NULL) {
216 mainh = 24;
217 free(cause);
220 /* Work out the other pane height. */
221 if (mainh + PANE_MINIMUM >= sy) {
222 if (sy <= PANE_MINIMUM + PANE_MINIMUM)
223 mainh = PANE_MINIMUM;
224 else
225 mainh = sy - PANE_MINIMUM;
226 otherh = PANE_MINIMUM;
227 } else {
228 s = options_get_string(w->options, "other-pane-height");
229 otherh = args_string_percentage(s, 0, sy, sy, &cause);
230 if (cause != NULL || otherh == 0) {
231 otherh = sy - mainh;
232 free(cause);
233 } else if (otherh > sy || sy - otherh < mainh)
234 otherh = sy - mainh;
235 else
236 mainh = sy - otherh;
239 /* Work out what width is needed. */
240 sx = (n * (PANE_MINIMUM + 1)) - 1;
241 if (sx < w->sx)
242 sx = w->sx;
244 /* Free old tree and create a new root. */
245 layout_free(w);
246 lc = w->layout_root = layout_create_cell(NULL);
247 layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
248 layout_make_node(lc, LAYOUT_TOPBOTTOM);
250 /* Create the main pane. */
251 lcmain = layout_create_cell(lc);
252 layout_set_size(lcmain, sx, mainh, 0, 0);
253 layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
254 TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
256 /* Create the other pane. */
257 lcother = layout_create_cell(lc);
258 layout_set_size(lcother, sx, otherh, 0, 0);
259 if (n == 1) {
260 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
261 layout_make_leaf(lcother, wp);
262 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
263 } else {
264 layout_make_node(lcother, LAYOUT_LEFTRIGHT);
265 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
267 /* Add the remaining panes as children. */
268 TAILQ_FOREACH(wp, &w->panes, entry) {
269 if (wp == TAILQ_FIRST(&w->panes))
270 continue;
271 lcchild = layout_create_cell(lcother);
272 layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
273 layout_make_leaf(lcchild, wp);
274 TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
276 layout_spread_cell(w, lcother);
279 /* Fix cell offsets. */
280 layout_fix_offsets(w);
281 layout_fix_panes(w, NULL);
283 layout_print_cell(w->layout_root, __func__, 1);
285 window_resize(w, lc->sx, lc->sy, -1, -1);
286 notify_window("window-layout-changed", w);
287 server_redraw_window(w);
290 static void
291 layout_set_main_h_mirrored(struct window *w)
293 struct window_pane *wp;
294 struct layout_cell *lc, *lcmain, *lcother, *lcchild;
295 u_int n, mainh, otherh, sx, sy;
296 char *cause;
297 const char *s;
299 layout_print_cell(w->layout_root, __func__, 1);
301 /* Get number of panes. */
302 n = window_count_panes(w);
303 if (n <= 1)
304 return;
305 n--; /* take off main pane */
307 /* Find available height - take off one line for the border. */
308 sy = w->sy - 1;
310 /* Get the main pane height. */
311 s = options_get_string(w->options, "main-pane-height");
312 mainh = args_string_percentage(s, 0, sy, sy, &cause);
313 if (cause != NULL) {
314 mainh = 24;
315 free(cause);
318 /* Work out the other pane height. */
319 if (mainh + PANE_MINIMUM >= sy) {
320 if (sy <= PANE_MINIMUM + PANE_MINIMUM)
321 mainh = PANE_MINIMUM;
322 else
323 mainh = sy - PANE_MINIMUM;
324 otherh = PANE_MINIMUM;
325 } else {
326 s = options_get_string(w->options, "other-pane-height");
327 otherh = args_string_percentage(s, 0, sy, sy, &cause);
328 if (cause != NULL || otherh == 0) {
329 otherh = sy - mainh;
330 free(cause);
331 } else if (otherh > sy || sy - otherh < mainh)
332 otherh = sy - mainh;
333 else
334 mainh = sy - otherh;
337 /* Work out what width is needed. */
338 sx = (n * (PANE_MINIMUM + 1)) - 1;
339 if (sx < w->sx)
340 sx = w->sx;
342 /* Free old tree and create a new root. */
343 layout_free(w);
344 lc = w->layout_root = layout_create_cell(NULL);
345 layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
346 layout_make_node(lc, LAYOUT_TOPBOTTOM);
348 /* Create the other pane. */
349 lcother = layout_create_cell(lc);
350 layout_set_size(lcother, sx, otherh, 0, 0);
351 if (n == 1) {
352 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
353 layout_make_leaf(lcother, wp);
354 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
355 } else {
356 layout_make_node(lcother, LAYOUT_LEFTRIGHT);
357 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
359 /* Add the remaining panes as children. */
360 TAILQ_FOREACH(wp, &w->panes, entry) {
361 if (wp == TAILQ_FIRST(&w->panes))
362 continue;
363 lcchild = layout_create_cell(lcother);
364 layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
365 layout_make_leaf(lcchild, wp);
366 TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
368 layout_spread_cell(w, lcother);
371 /* Create the main pane. */
372 lcmain = layout_create_cell(lc);
373 layout_set_size(lcmain, sx, mainh, 0, 0);
374 layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
375 TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
377 /* Fix cell offsets. */
378 layout_fix_offsets(w);
379 layout_fix_panes(w, NULL);
381 layout_print_cell(w->layout_root, __func__, 1);
383 window_resize(w, lc->sx, lc->sy, -1, -1);
384 notify_window("window-layout-changed", w);
385 server_redraw_window(w);
388 static void
389 layout_set_main_v(struct window *w)
391 struct window_pane *wp;
392 struct layout_cell *lc, *lcmain, *lcother, *lcchild;
393 u_int n, mainw, otherw, sx, sy;
394 char *cause;
395 const char *s;
397 layout_print_cell(w->layout_root, __func__, 1);
399 /* Get number of panes. */
400 n = window_count_panes(w);
401 if (n <= 1)
402 return;
403 n--; /* take off main pane */
405 /* Find available width - take off one line for the border. */
406 sx = w->sx - 1;
408 /* Get the main pane width. */
409 s = options_get_string(w->options, "main-pane-width");
410 mainw = args_string_percentage(s, 0, sx, sx, &cause);
411 if (cause != NULL) {
412 mainw = 80;
413 free(cause);
416 /* Work out the other pane width. */
417 if (mainw + PANE_MINIMUM >= sx) {
418 if (sx <= PANE_MINIMUM + PANE_MINIMUM)
419 mainw = PANE_MINIMUM;
420 else
421 mainw = sx - PANE_MINIMUM;
422 otherw = PANE_MINIMUM;
423 } else {
424 s = options_get_string(w->options, "other-pane-width");
425 otherw = args_string_percentage(s, 0, sx, sx, &cause);
426 if (cause != NULL || otherw == 0) {
427 otherw = sx - mainw;
428 free(cause);
429 } else if (otherw > sx || sx - otherw < mainw)
430 otherw = sx - mainw;
431 else
432 mainw = sx - otherw;
435 /* Work out what height is needed. */
436 sy = (n * (PANE_MINIMUM + 1)) - 1;
437 if (sy < w->sy)
438 sy = w->sy;
440 /* Free old tree and create a new root. */
441 layout_free(w);
442 lc = w->layout_root = layout_create_cell(NULL);
443 layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
444 layout_make_node(lc, LAYOUT_LEFTRIGHT);
446 /* Create the main pane. */
447 lcmain = layout_create_cell(lc);
448 layout_set_size(lcmain, mainw, sy, 0, 0);
449 layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
450 TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
452 /* Create the other pane. */
453 lcother = layout_create_cell(lc);
454 layout_set_size(lcother, otherw, sy, 0, 0);
455 if (n == 1) {
456 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
457 layout_make_leaf(lcother, wp);
458 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
459 } else {
460 layout_make_node(lcother, LAYOUT_TOPBOTTOM);
461 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
463 /* Add the remaining panes as children. */
464 TAILQ_FOREACH(wp, &w->panes, entry) {
465 if (wp == TAILQ_FIRST(&w->panes))
466 continue;
467 lcchild = layout_create_cell(lcother);
468 layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
469 layout_make_leaf(lcchild, wp);
470 TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
472 layout_spread_cell(w, lcother);
475 /* Fix cell offsets. */
476 layout_fix_offsets(w);
477 layout_fix_panes(w, NULL);
479 layout_print_cell(w->layout_root, __func__, 1);
481 window_resize(w, lc->sx, lc->sy, -1, -1);
482 notify_window("window-layout-changed", w);
483 server_redraw_window(w);
486 static void
487 layout_set_main_v_mirrored(struct window *w)
489 struct window_pane *wp;
490 struct layout_cell *lc, *lcmain, *lcother, *lcchild;
491 u_int n, mainw, otherw, sx, sy;
492 char *cause;
493 const char *s;
495 layout_print_cell(w->layout_root, __func__, 1);
497 /* Get number of panes. */
498 n = window_count_panes(w);
499 if (n <= 1)
500 return;
501 n--; /* take off main pane */
503 /* Find available width - take off one line for the border. */
504 sx = w->sx - 1;
506 /* Get the main pane width. */
507 s = options_get_string(w->options, "main-pane-width");
508 mainw = args_string_percentage(s, 0, sx, sx, &cause);
509 if (cause != NULL) {
510 mainw = 80;
511 free(cause);
514 /* Work out the other pane width. */
515 if (mainw + PANE_MINIMUM >= sx) {
516 if (sx <= PANE_MINIMUM + PANE_MINIMUM)
517 mainw = PANE_MINIMUM;
518 else
519 mainw = sx - PANE_MINIMUM;
520 otherw = PANE_MINIMUM;
521 } else {
522 s = options_get_string(w->options, "other-pane-width");
523 otherw = args_string_percentage(s, 0, sx, sx, &cause);
524 if (cause != NULL || otherw == 0) {
525 otherw = sx - mainw;
526 free(cause);
527 } else if (otherw > sx || sx - otherw < mainw)
528 otherw = sx - mainw;
529 else
530 mainw = sx - otherw;
533 /* Work out what height is needed. */
534 sy = (n * (PANE_MINIMUM + 1)) - 1;
535 if (sy < w->sy)
536 sy = w->sy;
538 /* Free old tree and create a new root. */
539 layout_free(w);
540 lc = w->layout_root = layout_create_cell(NULL);
541 layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
542 layout_make_node(lc, LAYOUT_LEFTRIGHT);
544 /* Create the other pane. */
545 lcother = layout_create_cell(lc);
546 layout_set_size(lcother, otherw, sy, 0, 0);
547 if (n == 1) {
548 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
549 layout_make_leaf(lcother, wp);
550 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
551 } else {
552 layout_make_node(lcother, LAYOUT_TOPBOTTOM);
553 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
555 /* Add the remaining panes as children. */
556 TAILQ_FOREACH(wp, &w->panes, entry) {
557 if (wp == TAILQ_FIRST(&w->panes))
558 continue;
559 lcchild = layout_create_cell(lcother);
560 layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
561 layout_make_leaf(lcchild, wp);
562 TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
564 layout_spread_cell(w, lcother);
567 /* Create the main pane. */
568 lcmain = layout_create_cell(lc);
569 layout_set_size(lcmain, mainw, sy, 0, 0);
570 layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
571 TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
573 /* Fix cell offsets. */
574 layout_fix_offsets(w);
575 layout_fix_panes(w, NULL);
577 layout_print_cell(w->layout_root, __func__, 1);
579 window_resize(w, lc->sx, lc->sy, -1, -1);
580 notify_window("window-layout-changed", w);
581 server_redraw_window(w);
584 void
585 layout_set_tiled(struct window *w)
587 struct window_pane *wp;
588 struct layout_cell *lc, *lcrow, *lcchild;
589 u_int n, width, height, used, sx, sy;
590 u_int i, j, columns, rows;
592 layout_print_cell(w->layout_root, __func__, 1);
594 /* Get number of panes. */
595 n = window_count_panes(w);
596 if (n <= 1)
597 return;
599 /* How many rows and columns are wanted? */
600 rows = columns = 1;
601 while (rows * columns < n) {
602 rows++;
603 if (rows * columns < n)
604 columns++;
607 /* What width and height should they be? */
608 width = (w->sx - (columns - 1)) / columns;
609 if (width < PANE_MINIMUM)
610 width = PANE_MINIMUM;
611 height = (w->sy - (rows - 1)) / rows;
612 if (height < PANE_MINIMUM)
613 height = PANE_MINIMUM;
615 /* Free old tree and create a new root. */
616 layout_free(w);
617 lc = w->layout_root = layout_create_cell(NULL);
618 sx = ((width + 1) * columns) - 1;
619 if (sx < w->sx)
620 sx = w->sx;
621 sy = ((height + 1) * rows) - 1;
622 if (sy < w->sy)
623 sy = w->sy;
624 layout_set_size(lc, sx, sy, 0, 0);
625 layout_make_node(lc, LAYOUT_TOPBOTTOM);
627 /* Create a grid of the cells. */
628 wp = TAILQ_FIRST(&w->panes);
629 for (j = 0; j < rows; j++) {
630 /* If this is the last cell, all done. */
631 if (wp == NULL)
632 break;
634 /* Create the new row. */
635 lcrow = layout_create_cell(lc);
636 layout_set_size(lcrow, w->sx, height, 0, 0);
637 TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
639 /* If only one column, just use the row directly. */
640 if (n - (j * columns) == 1 || columns == 1) {
641 layout_make_leaf(lcrow, wp);
642 wp = TAILQ_NEXT(wp, entry);
643 continue;
646 /* Add in the columns. */
647 layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
648 for (i = 0; i < columns; i++) {
649 /* Create and add a pane cell. */
650 lcchild = layout_create_cell(lcrow);
651 layout_set_size(lcchild, width, height, 0, 0);
652 layout_make_leaf(lcchild, wp);
653 TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
655 /* Move to the next cell. */
656 if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
657 break;
661 * Adjust the row and columns to fit the full width if
662 * necessary.
664 if (i == columns)
665 i--;
666 used = ((i + 1) * (width + 1)) - 1;
667 if (w->sx <= used)
668 continue;
669 lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
670 layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT,
671 w->sx - used);
674 /* Adjust the last row height to fit if necessary. */
675 used = (rows * height) + rows - 1;
676 if (w->sy > used) {
677 lcrow = TAILQ_LAST(&lc->cells, layout_cells);
678 layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
679 w->sy - used);
682 /* Fix cell offsets. */
683 layout_fix_offsets(w);
684 layout_fix_panes(w, NULL);
686 layout_print_cell(w->layout_root, __func__, 1);
688 window_resize(w, lc->sx, lc->sy, -1, -1);
689 notify_window("window-layout-changed", w);
690 server_redraw_window(w);