Annotation of src/usr.bin/tmux/layout-set.c, Revision 1.24
1.24 ! nicm 1: /* $OpenBSD: layout-set.c,v 1.23 2019/04/17 14:43:49 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:
! 216: /* Work out what height is needed. */
! 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.24 ! nicm 224: layout_set_size(lc, sx, mainh + otherh + 1, 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);
! 236: layout_make_node(lcother, LAYOUT_LEFTRIGHT);
! 237: TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
1.1 nicm 238:
1.24 ! nicm 239: /* Add the remaining panes as children. */
! 240: TAILQ_FOREACH(wp, &w->panes, entry) {
! 241: if (wp == TAILQ_FIRST(&w->panes))
1.4 nicm 242: continue;
1.24 ! nicm 243: lcchild = layout_create_cell(lc);
! 244: layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
! 245: layout_make_leaf(lcchild, wp);
! 246: TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
1.1 nicm 247: }
1.24 ! nicm 248: layout_spread_cell(w, lcother);
1.1 nicm 249:
250: /* Fix cell offsets. */
251: layout_fix_offsets(lc);
1.21 nicm 252: layout_fix_panes(w);
1.1 nicm 253:
254: layout_print_cell(w->layout_root, __func__, 1);
255:
1.23 nicm 256: window_resize(w, lc->sx, lc->sy);
1.18 nicm 257: notify_window("window-layout-changed", w);
1.1 nicm 258: server_redraw_window(w);
259: }
260:
1.16 nicm 261: static void
1.1 nicm 262: layout_set_main_v(struct window *w)
263: {
264: struct window_pane *wp;
1.24 ! nicm 265: struct layout_cell *lc, *lcmain, *lcother, *lcchild;
! 266: u_int n, mainw, otherw, sy;
1.1 nicm 267:
268: layout_print_cell(w->layout_root, __func__, 1);
269:
270: /* Get number of panes. */
271: n = window_count_panes(w);
272: if (n <= 1)
273: return;
274: n--; /* take off main pane */
275:
1.24 ! nicm 276: /* Get the main pane width and work out the other pane width. */
! 277: mainw = options_get_number(w->options, "main-pane-width");
! 278: if (mainw + PANE_MINIMUM + 1 >= w->sx) {
! 279: if (w->sx <= PANE_MINIMUM + 1 + PANE_MINIMUM)
! 280: mainw = PANE_MINIMUM;
1.1 nicm 281: else
1.24 ! nicm 282: mainw = w->sx - (PANE_MINIMUM + 1);
! 283: otherw = PANE_MINIMUM;
! 284: } else {
! 285: otherw = options_get_number(w->options, "other-pane-width");
! 286: if (otherw == 0)
! 287: otherw = w->sx - mainw;
! 288: else if (otherw > w->sx || w->sx - otherw < mainw)
! 289: otherw = w->sx - mainw;
! 290: else
! 291: mainw = w->sx - otherw;
! 292: }
! 293:
! 294: /* Work out what height is needed. */
! 295: sy = (n * (PANE_MINIMUM + 1)) - 1;
! 296: if (sy < w->sy)
! 297: sy = w->sy;
1.1 nicm 298:
299: /* Free old tree and create a new root. */
300: layout_free(w);
301: lc = w->layout_root = layout_create_cell(NULL);
1.24 ! nicm 302: layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
1.1 nicm 303: layout_make_node(lc, LAYOUT_LEFTRIGHT);
304:
305: /* Create the main pane. */
306: lcmain = layout_create_cell(lc);
1.24 ! nicm 307: layout_set_size(lcmain, mainw, sy, 0, 0);
1.1 nicm 308: layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
309: TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
310:
1.24 ! nicm 311: /* Create the other pane. */
! 312: lcother = layout_create_cell(lc);
! 313: layout_set_size(lcother, otherw, sy, 0, 0);
! 314: layout_make_node(lcother, LAYOUT_TOPBOTTOM);
! 315: TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
1.1 nicm 316:
1.24 ! nicm 317: /* Add the remaining panes as children. */
! 318: TAILQ_FOREACH(wp, &w->panes, entry) {
! 319: if (wp == TAILQ_FIRST(&w->panes))
1.4 nicm 320: continue;
1.24 ! nicm 321: lcchild = layout_create_cell(lc);
! 322: layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
! 323: layout_make_leaf(lcchild, wp);
! 324: TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
1.6 nicm 325: }
1.24 ! nicm 326: layout_spread_cell(w, lcother);
1.6 nicm 327:
328: /* Fix cell offsets. */
329: layout_fix_offsets(lc);
1.21 nicm 330: layout_fix_panes(w);
1.6 nicm 331:
332: layout_print_cell(w->layout_root, __func__, 1);
333:
1.23 nicm 334: window_resize(w, lc->sx, lc->sy);
1.18 nicm 335: notify_window("window-layout-changed", w);
1.6 nicm 336: server_redraw_window(w);
337: }
338:
339: void
340: layout_set_tiled(struct window *w)
341: {
342: struct window_pane *wp;
343: struct layout_cell *lc, *lcrow, *lcchild;
1.23 nicm 344: u_int n, width, height, used, sx, sy;
1.6 nicm 345: u_int i, j, columns, rows;
346:
347: layout_print_cell(w->layout_root, __func__, 1);
348:
349: /* Get number of panes. */
350: n = window_count_panes(w);
351: if (n <= 1)
352: return;
353:
354: /* How many rows and columns are wanted? */
355: rows = columns = 1;
356: while (rows * columns < n) {
357: rows++;
358: if (rows * columns < n)
359: columns++;
360: }
361:
362: /* What width and height should they be? */
1.7 nicm 363: width = (w->sx - (columns - 1)) / columns;
364: if (width < PANE_MINIMUM)
365: width = PANE_MINIMUM;
366: height = (w->sy - (rows - 1)) / rows;
367: if (height < PANE_MINIMUM)
368: height = PANE_MINIMUM;
1.6 nicm 369:
370: /* Free old tree and create a new root. */
371: layout_free(w);
372: lc = w->layout_root = layout_create_cell(NULL);
1.23 nicm 373: sx = ((width + 1) * columns) - 1;
374: if (sx < w->sx)
375: sx = w->sx;
376: sy = ((height + 1) * rows) - 1;
377: if (sy < w->sy)
378: sy = w->sy;
379: layout_set_size(lc, sx, sy, 0, 0);
1.6 nicm 380: layout_make_node(lc, LAYOUT_TOPBOTTOM);
381:
382: /* Create a grid of the cells. */
383: wp = TAILQ_FIRST(&w->panes);
384: for (j = 0; j < rows; j++) {
385: /* If this is the last cell, all done. */
386: if (wp == NULL)
387: break;
388:
389: /* Create the new row. */
390: lcrow = layout_create_cell(lc);
1.7 nicm 391: layout_set_size(lcrow, w->sx, height, 0, 0);
1.6 nicm 392: TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
393:
394: /* If only one column, just use the row directly. */
1.9 nicm 395: if (n - (j * columns) == 1 || columns == 1) {
1.6 nicm 396: layout_make_leaf(lcrow, wp);
397: wp = TAILQ_NEXT(wp, entry);
398: continue;
399: }
400:
401: /* Add in the columns. */
402: layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
403: for (i = 0; i < columns; i++) {
404: /* Create and add a pane cell. */
405: lcchild = layout_create_cell(lcrow);
1.7 nicm 406: layout_set_size(lcchild, width, height, 0, 0);
1.6 nicm 407: layout_make_leaf(lcchild, wp);
408: TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
409:
410: /* Move to the next cell. */
411: if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
412: break;
413: }
414:
415: /*
416: * Adjust the row and columns to fit the full width if
417: * necessary.
418: */
419: if (i == columns)
420: i--;
1.7 nicm 421: used = ((i + 1) * (width + 1)) - 1;
1.6 nicm 422: if (w->sx <= used)
423: continue;
424: lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
1.15 nicm 425: layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT,
426: w->sx - used);
1.6 nicm 427: }
428:
429: /* Adjust the last row height to fit if necessary. */
1.7 nicm 430: used = (rows * height) + rows - 1;
1.6 nicm 431: if (w->sy > used) {
432: lcrow = TAILQ_LAST(&lc->cells, layout_cells);
1.15 nicm 433: layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
434: w->sy - used);
1.1 nicm 435: }
436:
437: /* Fix cell offsets. */
438: layout_fix_offsets(lc);
1.21 nicm 439: layout_fix_panes(w);
1.1 nicm 440:
441: layout_print_cell(w->layout_root, __func__, 1);
442:
1.23 nicm 443: window_resize(w, lc->sx, lc->sy);
1.18 nicm 444: notify_window("window-layout-changed", w);
1.1 nicm 445: server_redraw_window(w);
446: }