Annotation of src/usr.bin/tmux/layout-set.c, Revision 1.30
1.30 ! nicm 1: /* $OpenBSD: layout-set.c,v 1.29 2020/04/22 06:57:13 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:
1.29 nicm 21: #include <stdlib.h>
1.1 nicm 22: #include <string.h>
23:
24: #include "tmux.h"
25:
26: /*
1.11 nicm 27: * Set window layouts - predefined methods to arrange windows. These are
28: * one-off and generate a layout tree.
1.1 nicm 29: */
30:
1.16 nicm 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_v(struct window *);
35: static void layout_set_tiled(struct window *);
1.1 nicm 36:
1.17 nicm 37: static const struct {
1.1 nicm 38: const char *name;
39: void (*arrange)(struct window *);
40: } layout_sets[] = {
41: { "even-horizontal", layout_set_even_h },
42: { "even-vertical", layout_set_even_v },
43: { "main-horizontal", layout_set_main_h },
44: { "main-vertical", layout_set_main_v },
1.6 nicm 45: { "tiled", layout_set_tiled },
1.1 nicm 46: };
47:
48: int
49: layout_set_lookup(const char *name)
50: {
51: u_int i;
52: int matched = -1;
53:
54: for (i = 0; i < nitems(layout_sets); i++) {
55: if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
56: if (matched != -1) /* ambiguous */
57: return (-1);
58: matched = i;
59: }
60: }
61:
62: return (matched);
63: }
64:
65: u_int
66: layout_set_select(struct window *w, u_int layout)
67: {
68: if (layout > nitems(layout_sets) - 1)
69: layout = nitems(layout_sets) - 1;
70:
71: if (layout_sets[layout].arrange != NULL)
72: layout_sets[layout].arrange(w);
73:
1.3 nicm 74: w->lastlayout = layout;
1.1 nicm 75: return (layout);
76: }
77:
78: u_int
79: layout_set_next(struct window *w)
80: {
1.3 nicm 81: u_int layout;
82:
83: if (w->lastlayout == -1)
84: layout = 0;
85: else {
86: layout = w->lastlayout + 1;
87: if (layout > nitems(layout_sets) - 1)
88: layout = 0;
89: }
1.1 nicm 90:
91: if (layout_sets[layout].arrange != NULL)
92: layout_sets[layout].arrange(w);
1.3 nicm 93: w->lastlayout = layout;
1.1 nicm 94: return (layout);
95: }
96:
97: u_int
98: layout_set_previous(struct window *w)
99: {
1.3 nicm 100: u_int layout;
101:
102: if (w->lastlayout == -1)
103: layout = nitems(layout_sets) - 1;
104: else {
105: layout = w->lastlayout;
106: if (layout == 0)
107: layout = nitems(layout_sets) - 1;
108: else
109: layout--;
110: }
1.1 nicm 111:
112: if (layout_sets[layout].arrange != NULL)
113: layout_sets[layout].arrange(w);
1.3 nicm 114: w->lastlayout = layout;
1.1 nicm 115: return (layout);
116: }
117:
1.16 nicm 118: static void
1.19 nicm 119: layout_set_even(struct window *w, enum layout_type type)
1.1 nicm 120: {
121: struct window_pane *wp;
122: struct layout_cell *lc, *lcnew;
1.23 nicm 123: u_int n, sx, sy;
1.1 nicm 124:
125: layout_print_cell(w->layout_root, __func__, 1);
126:
127: /* Get number of panes. */
128: n = window_count_panes(w);
129: if (n <= 1)
130: return;
131:
132: /* Free the old root and construct a new. */
133: layout_free(w);
134: lc = w->layout_root = layout_create_cell(NULL);
1.23 nicm 135: if (type == LAYOUT_LEFTRIGHT) {
136: sx = (n * (PANE_MINIMUM + 1)) - 1;
137: if (sx < w->sx)
138: sx = w->sx;
139: sy = w->sy;
140: } else {
141: sy = (n * (PANE_MINIMUM + 1)) - 1;
142: if (sy < w->sy)
143: sy = w->sy;
144: sx = w->sx;
145: }
146: layout_set_size(lc, sx, sy, 0, 0);
1.19 nicm 147: layout_make_node(lc, type);
1.1 nicm 148:
149: /* Build new leaf cells. */
150: TAILQ_FOREACH(wp, &w->panes, entry) {
151: lcnew = layout_create_cell(lc);
152: layout_make_leaf(lcnew, wp);
1.20 nicm 153: lcnew->sx = w->sx;
154: lcnew->sy = w->sy;
1.1 nicm 155: TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
156: }
157:
1.19 nicm 158: /* Spread out cells. */
159: layout_spread_cell(w, lc);
1.1 nicm 160:
161: /* Fix cell offsets. */
1.27 nicm 162: layout_fix_offsets(w);
1.30 ! nicm 163: layout_fix_panes(w, NULL);
1.1 nicm 164:
165: layout_print_cell(w->layout_root, __func__, 1);
166:
1.28 nicm 167: window_resize(w, lc->sx, lc->sy, -1, -1);
1.18 nicm 168: notify_window("window-layout-changed", w);
1.1 nicm 169: server_redraw_window(w);
170: }
171:
1.16 nicm 172: static void
1.19 nicm 173: layout_set_even_h(struct window *w)
174: {
175: layout_set_even(w, LAYOUT_LEFTRIGHT);
176: }
177:
178: static void
1.1 nicm 179: layout_set_even_v(struct window *w)
180: {
1.19 nicm 181: layout_set_even(w, LAYOUT_TOPBOTTOM);
1.1 nicm 182: }
183:
1.16 nicm 184: static void
1.1 nicm 185: layout_set_main_h(struct window *w)
186: {
187: struct window_pane *wp;
1.24 nicm 188: struct layout_cell *lc, *lcmain, *lcother, *lcchild;
1.26 nicm 189: u_int n, mainh, otherh, sx, sy;
1.29 nicm 190: char *cause;
191: const char *s;
1.1 nicm 192:
193: layout_print_cell(w->layout_root, __func__, 1);
194:
195: /* Get number of panes. */
196: n = window_count_panes(w);
197: if (n <= 1)
198: return;
199: n--; /* take off main pane */
200:
1.26 nicm 201: /* Find available height - take off one line for the border. */
202: sy = w->sy - 1;
203:
1.29 nicm 204: /* Get the main pane height. */
205: s = options_get_string(w->options, "main-pane-height");
206: mainh = args_string_percentage(s, 0, sy, sy, &cause);
207: if (cause != NULL) {
208: mainh = 24;
209: free(cause);
210: }
211:
212: /* Work out the other pane height. */
1.26 nicm 213: if (mainh + PANE_MINIMUM >= sy) {
214: if (sy <= PANE_MINIMUM + PANE_MINIMUM)
1.24 nicm 215: mainh = PANE_MINIMUM;
1.1 nicm 216: else
1.26 nicm 217: mainh = sy - PANE_MINIMUM;
1.24 nicm 218: otherh = PANE_MINIMUM;
219: } else {
1.29 nicm 220: s = options_get_string(w->options, "other-pane-height");
221: otherh = args_string_percentage(s, 0, sy, sy, &cause);
222: if (cause != NULL || otherh == 0) {
1.26 nicm 223: otherh = sy - mainh;
1.29 nicm 224: free(cause);
225: } else if (otherh > sy || sy - otherh < mainh)
1.26 nicm 226: otherh = sy - mainh;
1.24 nicm 227: else
1.26 nicm 228: mainh = sy - otherh;
1.24 nicm 229: }
230:
1.25 nicm 231: /* Work out what width is needed. */
1.24 nicm 232: sx = (n * (PANE_MINIMUM + 1)) - 1;
233: if (sx < w->sx)
234: sx = w->sx;
1.1 nicm 235:
236: /* Free old tree and create a new root. */
237: layout_free(w);
238: lc = w->layout_root = layout_create_cell(NULL);
1.26 nicm 239: layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
1.1 nicm 240: layout_make_node(lc, LAYOUT_TOPBOTTOM);
241:
242: /* Create the main pane. */
243: lcmain = layout_create_cell(lc);
1.24 nicm 244: layout_set_size(lcmain, sx, mainh, 0, 0);
1.1 nicm 245: layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
246: TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
247:
1.24 nicm 248: /* Create the other pane. */
249: lcother = layout_create_cell(lc);
250: layout_set_size(lcother, sx, otherh, 0, 0);
1.25 nicm 251: if (n == 1) {
252: wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
253: layout_make_leaf(lcother, wp);
254: TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
255: } else {
256: layout_make_node(lcother, LAYOUT_LEFTRIGHT);
257: TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
1.1 nicm 258:
1.25 nicm 259: /* Add the remaining panes as children. */
260: TAILQ_FOREACH(wp, &w->panes, entry) {
261: if (wp == TAILQ_FIRST(&w->panes))
262: continue;
263: lcchild = layout_create_cell(lcother);
264: layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
265: layout_make_leaf(lcchild, wp);
266: TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
267: }
268: layout_spread_cell(w, lcother);
1.1 nicm 269: }
270:
271: /* Fix cell offsets. */
1.27 nicm 272: layout_fix_offsets(w);
1.30 ! nicm 273: layout_fix_panes(w, NULL);
1.1 nicm 274:
275: layout_print_cell(w->layout_root, __func__, 1);
276:
1.28 nicm 277: window_resize(w, lc->sx, lc->sy, -1, -1);
1.18 nicm 278: notify_window("window-layout-changed", w);
1.1 nicm 279: server_redraw_window(w);
280: }
281:
1.16 nicm 282: static void
1.1 nicm 283: layout_set_main_v(struct window *w)
284: {
285: struct window_pane *wp;
1.24 nicm 286: struct layout_cell *lc, *lcmain, *lcother, *lcchild;
1.26 nicm 287: u_int n, mainw, otherw, sx, sy;
1.29 nicm 288: char *cause;
289: const char *s;
1.1 nicm 290:
291: layout_print_cell(w->layout_root, __func__, 1);
292:
293: /* Get number of panes. */
294: n = window_count_panes(w);
295: if (n <= 1)
296: return;
297: n--; /* take off main pane */
298:
1.26 nicm 299: /* Find available width - take off one line for the border. */
300: sx = w->sx - 1;
301:
1.29 nicm 302: /* Get the main pane width. */
303: s = options_get_string(w->options, "main-pane-width");
304: mainw = args_string_percentage(s, 0, sx, sx, &cause);
305: if (cause != NULL) {
306: mainw = 80;
307: free(cause);
308: }
309:
310: /* Work out the other pane width. */
1.26 nicm 311: if (mainw + PANE_MINIMUM >= sx) {
312: if (sx <= PANE_MINIMUM + PANE_MINIMUM)
1.24 nicm 313: mainw = PANE_MINIMUM;
1.1 nicm 314: else
1.26 nicm 315: mainw = sx - PANE_MINIMUM;
1.24 nicm 316: otherw = PANE_MINIMUM;
317: } else {
1.29 nicm 318: s = options_get_string(w->options, "other-pane-width");
319: otherw = args_string_percentage(s, 0, sx, sx, &cause);
320: if (cause != NULL || otherw == 0) {
1.26 nicm 321: otherw = sx - mainw;
1.29 nicm 322: free(cause);
323: } else if (otherw > sx || sx - otherw < mainw)
1.26 nicm 324: otherw = sx - mainw;
1.24 nicm 325: else
1.26 nicm 326: mainw = sx - otherw;
1.24 nicm 327: }
328:
329: /* Work out what height is needed. */
330: sy = (n * (PANE_MINIMUM + 1)) - 1;
331: if (sy < w->sy)
332: sy = w->sy;
1.1 nicm 333:
334: /* Free old tree and create a new root. */
335: layout_free(w);
336: lc = w->layout_root = layout_create_cell(NULL);
1.26 nicm 337: layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
1.1 nicm 338: layout_make_node(lc, LAYOUT_LEFTRIGHT);
339:
340: /* Create the main pane. */
341: lcmain = layout_create_cell(lc);
1.24 nicm 342: layout_set_size(lcmain, mainw, sy, 0, 0);
1.1 nicm 343: layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
344: TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
345:
1.24 nicm 346: /* Create the other pane. */
347: lcother = layout_create_cell(lc);
348: layout_set_size(lcother, otherw, sy, 0, 0);
1.25 nicm 349: if (n == 1) {
350: wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
351: layout_make_leaf(lcother, wp);
352: TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
353: } else {
354: layout_make_node(lcother, LAYOUT_TOPBOTTOM);
355: TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
1.1 nicm 356:
1.25 nicm 357: /* Add the remaining panes as children. */
358: TAILQ_FOREACH(wp, &w->panes, entry) {
359: if (wp == TAILQ_FIRST(&w->panes))
360: continue;
361: lcchild = layout_create_cell(lcother);
362: layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
363: layout_make_leaf(lcchild, wp);
364: TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
365: }
366: layout_spread_cell(w, lcother);
1.6 nicm 367: }
368:
369: /* Fix cell offsets. */
1.27 nicm 370: layout_fix_offsets(w);
1.30 ! nicm 371: layout_fix_panes(w, NULL);
1.6 nicm 372:
373: layout_print_cell(w->layout_root, __func__, 1);
374:
1.28 nicm 375: window_resize(w, lc->sx, lc->sy, -1, -1);
1.18 nicm 376: notify_window("window-layout-changed", w);
1.6 nicm 377: server_redraw_window(w);
378: }
379:
380: void
381: layout_set_tiled(struct window *w)
382: {
383: struct window_pane *wp;
384: struct layout_cell *lc, *lcrow, *lcchild;
1.23 nicm 385: u_int n, width, height, used, sx, sy;
1.6 nicm 386: u_int i, j, columns, rows;
387:
388: layout_print_cell(w->layout_root, __func__, 1);
389:
390: /* Get number of panes. */
391: n = window_count_panes(w);
392: if (n <= 1)
393: return;
394:
395: /* How many rows and columns are wanted? */
396: rows = columns = 1;
397: while (rows * columns < n) {
398: rows++;
399: if (rows * columns < n)
400: columns++;
401: }
402:
403: /* What width and height should they be? */
1.7 nicm 404: width = (w->sx - (columns - 1)) / columns;
405: if (width < PANE_MINIMUM)
406: width = PANE_MINIMUM;
407: height = (w->sy - (rows - 1)) / rows;
408: if (height < PANE_MINIMUM)
409: height = PANE_MINIMUM;
1.6 nicm 410:
411: /* Free old tree and create a new root. */
412: layout_free(w);
413: lc = w->layout_root = layout_create_cell(NULL);
1.23 nicm 414: sx = ((width + 1) * columns) - 1;
415: if (sx < w->sx)
416: sx = w->sx;
417: sy = ((height + 1) * rows) - 1;
418: if (sy < w->sy)
419: sy = w->sy;
420: layout_set_size(lc, sx, sy, 0, 0);
1.6 nicm 421: layout_make_node(lc, LAYOUT_TOPBOTTOM);
422:
423: /* Create a grid of the cells. */
424: wp = TAILQ_FIRST(&w->panes);
425: for (j = 0; j < rows; j++) {
426: /* If this is the last cell, all done. */
427: if (wp == NULL)
428: break;
429:
430: /* Create the new row. */
431: lcrow = layout_create_cell(lc);
1.7 nicm 432: layout_set_size(lcrow, w->sx, height, 0, 0);
1.6 nicm 433: TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
434:
435: /* If only one column, just use the row directly. */
1.9 nicm 436: if (n - (j * columns) == 1 || columns == 1) {
1.6 nicm 437: layout_make_leaf(lcrow, wp);
438: wp = TAILQ_NEXT(wp, entry);
439: continue;
440: }
441:
442: /* Add in the columns. */
443: layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
444: for (i = 0; i < columns; i++) {
445: /* Create and add a pane cell. */
446: lcchild = layout_create_cell(lcrow);
1.7 nicm 447: layout_set_size(lcchild, width, height, 0, 0);
1.6 nicm 448: layout_make_leaf(lcchild, wp);
449: TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
450:
451: /* Move to the next cell. */
452: if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
453: break;
454: }
455:
456: /*
457: * Adjust the row and columns to fit the full width if
458: * necessary.
459: */
460: if (i == columns)
461: i--;
1.7 nicm 462: used = ((i + 1) * (width + 1)) - 1;
1.6 nicm 463: if (w->sx <= used)
464: continue;
465: lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
1.15 nicm 466: layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT,
467: w->sx - used);
1.6 nicm 468: }
469:
470: /* Adjust the last row height to fit if necessary. */
1.7 nicm 471: used = (rows * height) + rows - 1;
1.6 nicm 472: if (w->sy > used) {
473: lcrow = TAILQ_LAST(&lc->cells, layout_cells);
1.15 nicm 474: layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
475: w->sy - used);
1.1 nicm 476: }
477:
478: /* Fix cell offsets. */
1.27 nicm 479: layout_fix_offsets(w);
1.30 ! nicm 480: layout_fix_panes(w, NULL);
1.1 nicm 481:
482: layout_print_cell(w->layout_root, __func__, 1);
483:
1.28 nicm 484: window_resize(w, lc->sx, lc->sy, -1, -1);
1.18 nicm 485: notify_window("window-layout-changed", w);
1.1 nicm 486: server_redraw_window(w);
487: }