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