Annotation of src/usr.bin/tmux/layout-set.c, Revision 1.25
1.25 ! nicm 1: /* $OpenBSD: layout-set.c,v 1.24 2019/04/17 14:44:14 nicm Exp $ */
1.1 nicm 2:
3: /*
1.14 nicm 4: * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 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: /*
1.11 nicm 26: * Set window layouts - predefined methods to arrange windows. These are
27: * one-off and generate a layout tree.
1.1 nicm 28: */
29:
1.16 nicm 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 *);
1.1 nicm 35:
1.17 nicm 36: static const struct {
1.1 nicm 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 },
1.6 nicm 44: { "tiled", layout_set_tiled },
1.1 nicm 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:
1.3 nicm 73: w->lastlayout = layout;
1.1 nicm 74: return (layout);
75: }
76:
77: u_int
78: layout_set_next(struct window *w)
79: {
1.3 nicm 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: }
1.1 nicm 89:
90: if (layout_sets[layout].arrange != NULL)
91: layout_sets[layout].arrange(w);
1.3 nicm 92: w->lastlayout = layout;
1.1 nicm 93: return (layout);
94: }
95:
96: u_int
97: layout_set_previous(struct window *w)
98: {
1.3 nicm 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: }
1.1 nicm 110:
111: if (layout_sets[layout].arrange != NULL)
112: layout_sets[layout].arrange(w);
1.3 nicm 113: w->lastlayout = layout;
1.1 nicm 114: return (layout);
115: }
116:
1.16 nicm 117: static void
1.19 nicm 118: layout_set_even(struct window *w, enum layout_type type)
1.1 nicm 119: {
120: struct window_pane *wp;
121: struct layout_cell *lc, *lcnew;
1.23 nicm 122: u_int n, sx, sy;
1.1 nicm 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);
1.23 nicm 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);
1.19 nicm 146: layout_make_node(lc, type);
1.1 nicm 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);
1.20 nicm 152: lcnew->sx = w->sx;
153: lcnew->sy = w->sy;
1.1 nicm 154: TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
155: }
156:
1.19 nicm 157: /* Spread out cells. */
158: layout_spread_cell(w, lc);
1.1 nicm 159:
160: /* Fix cell offsets. */
161: layout_fix_offsets(lc);
1.21 nicm 162: layout_fix_panes(w);
1.1 nicm 163:
164: layout_print_cell(w->layout_root, __func__, 1);
165:
1.23 nicm 166: window_resize(w, lc->sx, lc->sy);
1.18 nicm 167: notify_window("window-layout-changed", w);
1.1 nicm 168: server_redraw_window(w);
169: }
170:
1.16 nicm 171: static void
1.19 nicm 172: layout_set_even_h(struct window *w)
173: {
174: layout_set_even(w, LAYOUT_LEFTRIGHT);
175: }
176:
177: static void
1.1 nicm 178: layout_set_even_v(struct window *w)
179: {
1.19 nicm 180: layout_set_even(w, LAYOUT_TOPBOTTOM);
1.1 nicm 181: }
182:
1.16 nicm 183: static void
1.1 nicm 184: layout_set_main_h(struct window *w)
185: {
186: struct window_pane *wp;
1.24 nicm 187: struct layout_cell *lc, *lcmain, *lcother, *lcchild;
188: u_int n, mainh, otherh, sx;
1.1 nicm 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:
1.24 nicm 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;
1.1 nicm 203: else
1.24 nicm 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:
1.25 ! nicm 216: /* Work out what width is needed. */
1.24 nicm 217: sx = (n * (PANE_MINIMUM + 1)) - 1;
218: if (sx < w->sx)
219: sx = w->sx;
1.1 nicm 220:
221: /* Free old tree and create a new root. */
222: layout_free(w);
223: lc = w->layout_root = layout_create_cell(NULL);
1.25 ! nicm 224: layout_set_size(lc, sx, mainh + otherh, 0, 0);
1.1 nicm 225: layout_make_node(lc, LAYOUT_TOPBOTTOM);
226:
227: /* Create the main pane. */
228: lcmain = layout_create_cell(lc);
1.24 nicm 229: layout_set_size(lcmain, sx, mainh, 0, 0);
1.1 nicm 230: layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
231: TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
232:
1.24 nicm 233: /* Create the other pane. */
234: lcother = layout_create_cell(lc);
235: layout_set_size(lcother, sx, otherh, 0, 0);
1.25 ! nicm 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);
1.1 nicm 243:
1.25 ! nicm 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);
1.1 nicm 254: }
255:
256: /* Fix cell offsets. */
257: layout_fix_offsets(lc);
1.21 nicm 258: layout_fix_panes(w);
1.1 nicm 259:
260: layout_print_cell(w->layout_root, __func__, 1);
261:
1.23 nicm 262: window_resize(w, lc->sx, lc->sy);
1.18 nicm 263: notify_window("window-layout-changed", w);
1.1 nicm 264: server_redraw_window(w);
265: }
266:
1.16 nicm 267: static void
1.1 nicm 268: layout_set_main_v(struct window *w)
269: {
270: struct window_pane *wp;
1.24 nicm 271: struct layout_cell *lc, *lcmain, *lcother, *lcchild;
272: u_int n, mainw, otherw, sy;
1.1 nicm 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:
1.24 nicm 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;
1.1 nicm 287: else
1.24 nicm 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;
1.1 nicm 304:
305: /* Free old tree and create a new root. */
306: layout_free(w);
307: lc = w->layout_root = layout_create_cell(NULL);
1.25 ! nicm 308: layout_set_size(lc, mainw + otherw, sy, 0, 0);
1.1 nicm 309: layout_make_node(lc, LAYOUT_LEFTRIGHT);
310:
311: /* Create the main pane. */
312: lcmain = layout_create_cell(lc);
1.24 nicm 313: layout_set_size(lcmain, mainw, sy, 0, 0);
1.1 nicm 314: layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
315: TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
316:
1.24 nicm 317: /* Create the other pane. */
318: lcother = layout_create_cell(lc);
319: layout_set_size(lcother, otherw, sy, 0, 0);
1.25 ! nicm 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);
1.1 nicm 327:
1.25 ! nicm 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);
1.6 nicm 338: }
339:
340: /* Fix cell offsets. */
341: layout_fix_offsets(lc);
1.21 nicm 342: layout_fix_panes(w);
1.6 nicm 343:
344: layout_print_cell(w->layout_root, __func__, 1);
345:
1.23 nicm 346: window_resize(w, lc->sx, lc->sy);
1.18 nicm 347: notify_window("window-layout-changed", w);
1.6 nicm 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;
1.23 nicm 356: u_int n, width, height, used, sx, sy;
1.6 nicm 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? */
1.7 nicm 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;
1.6 nicm 381:
382: /* Free old tree and create a new root. */
383: layout_free(w);
384: lc = w->layout_root = layout_create_cell(NULL);
1.23 nicm 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);
1.6 nicm 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);
1.7 nicm 403: layout_set_size(lcrow, w->sx, height, 0, 0);
1.6 nicm 404: TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
405:
406: /* If only one column, just use the row directly. */
1.9 nicm 407: if (n - (j * columns) == 1 || columns == 1) {
1.6 nicm 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);
1.7 nicm 418: layout_set_size(lcchild, width, height, 0, 0);
1.6 nicm 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--;
1.7 nicm 433: used = ((i + 1) * (width + 1)) - 1;
1.6 nicm 434: if (w->sx <= used)
435: continue;
436: lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
1.15 nicm 437: layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT,
438: w->sx - used);
1.6 nicm 439: }
440:
441: /* Adjust the last row height to fit if necessary. */
1.7 nicm 442: used = (rows * height) + rows - 1;
1.6 nicm 443: if (w->sy > used) {
444: lcrow = TAILQ_LAST(&lc->cells, layout_cells);
1.15 nicm 445: layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
446: w->sy - used);
1.1 nicm 447: }
448:
449: /* Fix cell offsets. */
450: layout_fix_offsets(lc);
1.21 nicm 451: layout_fix_panes(w);
1.1 nicm 452:
453: layout_print_cell(w->layout_root, __func__, 1);
454:
1.23 nicm 455: window_resize(w, lc->sx, lc->sy);
1.18 nicm 456: notify_window("window-layout-changed", w);
1.1 nicm 457: server_redraw_window(w);
458: }