Annotation of src/usr.bin/tmux/layout-set.c, Revision 1.23
1.23 ! nicm 1: /* $OpenBSD: layout-set.c,v 1.22 2019/04/04 10:25:35 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;
187: struct layout_cell *lc, *lcmain, *lcrow, *lcchild;
1.8 nicm 188: u_int n, mainheight, otherheight, width, height;
189: u_int used, i, j, columns, rows, totalrows;
1.1 nicm 190:
191: layout_print_cell(w->layout_root, __func__, 1);
192:
193: /* Get number of panes. */
194: n = window_count_panes(w);
195: if (n <= 1)
196: return;
197: n--; /* take off main pane */
198:
1.7 nicm 199: /* How many rows and columns will be needed, not counting main? */
200: columns = (w->sx + 1) / (PANE_MINIMUM + 1); /* maximum columns */
1.5 nicm 201: if (columns == 0)
202: columns = 1;
1.1 nicm 203: rows = 1 + (n - 1) / columns;
204: columns = 1 + (n - 1) / rows;
1.7 nicm 205: width = (w->sx - (n - 1)) / columns;
1.1 nicm 206:
207: /* Get the main pane height and add one for separator line. */
1.13 nicm 208: mainheight = options_get_number(w->options, "main-pane-height") + 1;
1.8 nicm 209:
210: /* Get the optional other pane height and add one for separator line. */
1.13 nicm 211: otherheight = options_get_number(w->options, "other-pane-height") + 1;
1.8 nicm 212:
213: /*
214: * If an other pane height was specified, honour it so long as it
215: * doesn't shrink the main height to less than the main-pane-height
216: */
1.10 nicm 217: if (otherheight > 1 && w->sy - otherheight > mainheight)
218: mainheight = w->sy - otherheight;
1.1 nicm 219: if (mainheight < PANE_MINIMUM + 1)
220: mainheight = PANE_MINIMUM + 1;
221:
222: /* Try and make everything fit. */
223: totalrows = rows * (PANE_MINIMUM + 1) - 1;
224: if (mainheight + totalrows > w->sy) {
225: if (totalrows + PANE_MINIMUM + 1 > w->sy)
226: mainheight = PANE_MINIMUM + 2;
227: else
228: mainheight = w->sy - totalrows;
1.7 nicm 229: height = PANE_MINIMUM;
1.1 nicm 230: } else
1.7 nicm 231: height = (w->sy - mainheight - (rows - 1)) / rows;
1.1 nicm 232:
233: /* Free old tree and create a new root. */
234: layout_free(w);
235: lc = w->layout_root = layout_create_cell(NULL);
1.7 nicm 236: layout_set_size(lc, w->sx, mainheight + rows * (height + 1) - 1, 0, 0);
1.1 nicm 237: layout_make_node(lc, LAYOUT_TOPBOTTOM);
238:
239: /* Create the main pane. */
240: lcmain = layout_create_cell(lc);
241: layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0);
242: layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
243: TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
244:
245: /* Create a grid of the remaining cells. */
246: wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
247: for (j = 0; j < rows; j++) {
248: /* If this is the last cell, all done. */
249: if (wp == NULL)
250: break;
251:
252: /* Create the new row. */
253: lcrow = layout_create_cell(lc);
1.7 nicm 254: layout_set_size(lcrow, w->sx, height, 0, 0);
1.1 nicm 255: TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
256:
257: /* If only one column, just use the row directly. */
258: if (columns == 1) {
259: layout_make_leaf(lcrow, wp);
260: wp = TAILQ_NEXT(wp, entry);
261: continue;
262: }
263:
1.4 nicm 264: /* Add in the columns. */
1.1 nicm 265: layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
266: for (i = 0; i < columns; i++) {
267: /* Create and add a pane cell. */
268: lcchild = layout_create_cell(lcrow);
1.7 nicm 269: layout_set_size(lcchild, width, height, 0, 0);
1.1 nicm 270: layout_make_leaf(lcchild, wp);
271: TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
272:
273: /* Move to the next cell. */
274: if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
275: break;
276: }
277:
278: /* Adjust the row to fit the full width if necessary. */
279: if (i == columns)
280: i--;
1.7 nicm 281: used = ((i + 1) * (width + 1)) - 1;
1.4 nicm 282: if (w->sx <= used)
283: continue;
284: lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
1.15 nicm 285: layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT,
286: w->sx - used);
1.1 nicm 287: }
288:
289: /* Adjust the last row height to fit if necessary. */
1.7 nicm 290: used = mainheight + (rows * height) + rows - 1;
1.1 nicm 291: if (w->sy > used) {
292: lcrow = TAILQ_LAST(&lc->cells, layout_cells);
1.15 nicm 293: layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
294: w->sy - used);
1.1 nicm 295: }
296:
297: /* Fix cell offsets. */
298: layout_fix_offsets(lc);
1.21 nicm 299: layout_fix_panes(w);
1.1 nicm 300:
301: layout_print_cell(w->layout_root, __func__, 1);
302:
1.23 ! nicm 303: window_resize(w, lc->sx, lc->sy);
1.18 nicm 304: notify_window("window-layout-changed", w);
1.1 nicm 305: server_redraw_window(w);
306: }
307:
1.16 nicm 308: static void
1.1 nicm 309: layout_set_main_v(struct window *w)
310: {
311: struct window_pane *wp;
312: struct layout_cell *lc, *lcmain, *lccolumn, *lcchild;
1.8 nicm 313: u_int n, mainwidth, otherwidth, width, height;
314: u_int used, i, j, columns, rows, totalcolumns;
1.1 nicm 315:
316: layout_print_cell(w->layout_root, __func__, 1);
317:
318: /* Get number of panes. */
319: n = window_count_panes(w);
320: if (n <= 1)
321: return;
322: n--; /* take off main pane */
323:
1.7 nicm 324: /* How many rows and columns will be needed, not counting main? */
325: rows = (w->sy + 1) / (PANE_MINIMUM + 1); /* maximum rows */
1.5 nicm 326: if (rows == 0)
327: rows = 1;
1.1 nicm 328: columns = 1 + (n - 1) / rows;
329: rows = 1 + (n - 1) / columns;
1.7 nicm 330: height = (w->sy - (n - 1)) / rows;
1.1 nicm 331:
332: /* Get the main pane width and add one for separator line. */
1.13 nicm 333: mainwidth = options_get_number(w->options, "main-pane-width") + 1;
1.8 nicm 334:
335: /* Get the optional other pane width and add one for separator line. */
1.13 nicm 336: otherwidth = options_get_number(w->options, "other-pane-width") + 1;
1.8 nicm 337:
338: /*
339: * If an other pane width was specified, honour it so long as it
340: * doesn't shrink the main width to less than the main-pane-width
341: */
342: if (otherwidth > 1 && w->sx - otherwidth > mainwidth)
343: mainwidth = w->sx - otherwidth;
1.1 nicm 344: if (mainwidth < PANE_MINIMUM + 1)
345: mainwidth = PANE_MINIMUM + 1;
346:
347: /* Try and make everything fit. */
348: totalcolumns = columns * (PANE_MINIMUM + 1) - 1;
349: if (mainwidth + totalcolumns > w->sx) {
350: if (totalcolumns + PANE_MINIMUM + 1 > w->sx)
351: mainwidth = PANE_MINIMUM + 2;
352: else
353: mainwidth = w->sx - totalcolumns;
1.7 nicm 354: width = PANE_MINIMUM;
1.1 nicm 355: } else
1.7 nicm 356: width = (w->sx - mainwidth - (columns - 1)) / columns;
1.1 nicm 357:
358: /* Free old tree and create a new root. */
359: layout_free(w);
360: lc = w->layout_root = layout_create_cell(NULL);
1.7 nicm 361: layout_set_size(lc, mainwidth + columns * (width + 1) - 1, w->sy, 0, 0);
1.1 nicm 362: layout_make_node(lc, LAYOUT_LEFTRIGHT);
363:
364: /* Create the main pane. */
365: lcmain = layout_create_cell(lc);
366: layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0);
367: layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
368: TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
369:
370: /* Create a grid of the remaining cells. */
371: wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
372: for (j = 0; j < columns; j++) {
373: /* If this is the last cell, all done. */
374: if (wp == NULL)
375: break;
376:
377: /* Create the new column. */
378: lccolumn = layout_create_cell(lc);
1.7 nicm 379: layout_set_size(lccolumn, width, w->sy, 0, 0);
1.1 nicm 380: TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry);
381:
382: /* If only one row, just use the row directly. */
383: if (rows == 1) {
384: layout_make_leaf(lccolumn, wp);
385: wp = TAILQ_NEXT(wp, entry);
386: continue;
387: }
388:
389: /* Add in the rows. */
390: layout_make_node(lccolumn, LAYOUT_TOPBOTTOM);
391: for (i = 0; i < rows; i++) {
392: /* Create and add a pane cell. */
393: lcchild = layout_create_cell(lccolumn);
1.7 nicm 394: layout_set_size(lcchild, width, height, 0, 0);
1.1 nicm 395: layout_make_leaf(lcchild, wp);
396: TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry);
397:
398: /* Move to the next cell. */
399: if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
400: break;
401: }
402:
403: /* Adjust the column to fit the full height if necessary. */
404: if (i == rows)
405: i--;
1.7 nicm 406: used = ((i + 1) * (height + 1)) - 1;
1.4 nicm 407: if (w->sy <= used)
408: continue;
409: lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells);
1.15 nicm 410: layout_resize_adjust(w, lcchild, LAYOUT_TOPBOTTOM,
411: w->sy - used);
1.1 nicm 412: }
413:
414: /* Adjust the last column width to fit if necessary. */
1.7 nicm 415: used = mainwidth + (columns * width) + columns - 1;
1.1 nicm 416: if (w->sx > used) {
417: lccolumn = TAILQ_LAST(&lc->cells, layout_cells);
1.15 nicm 418: layout_resize_adjust(w, lccolumn, LAYOUT_LEFTRIGHT,
419: w->sx - used);
1.6 nicm 420: }
421:
422: /* Fix cell offsets. */
423: layout_fix_offsets(lc);
1.21 nicm 424: layout_fix_panes(w);
1.6 nicm 425:
426: layout_print_cell(w->layout_root, __func__, 1);
427:
1.23 ! nicm 428: window_resize(w, lc->sx, lc->sy);
1.18 nicm 429: notify_window("window-layout-changed", w);
1.6 nicm 430: server_redraw_window(w);
431: }
432:
433: void
434: layout_set_tiled(struct window *w)
435: {
436: struct window_pane *wp;
437: struct layout_cell *lc, *lcrow, *lcchild;
1.23 ! nicm 438: u_int n, width, height, used, sx, sy;
1.6 nicm 439: u_int i, j, columns, rows;
440:
441: layout_print_cell(w->layout_root, __func__, 1);
442:
443: /* Get number of panes. */
444: n = window_count_panes(w);
445: if (n <= 1)
446: return;
447:
448: /* How many rows and columns are wanted? */
449: rows = columns = 1;
450: while (rows * columns < n) {
451: rows++;
452: if (rows * columns < n)
453: columns++;
454: }
455:
456: /* What width and height should they be? */
1.7 nicm 457: width = (w->sx - (columns - 1)) / columns;
458: if (width < PANE_MINIMUM)
459: width = PANE_MINIMUM;
460: height = (w->sy - (rows - 1)) / rows;
461: if (height < PANE_MINIMUM)
462: height = PANE_MINIMUM;
1.6 nicm 463:
464: /* Free old tree and create a new root. */
465: layout_free(w);
466: lc = w->layout_root = layout_create_cell(NULL);
1.23 ! nicm 467: sx = ((width + 1) * columns) - 1;
! 468: if (sx < w->sx)
! 469: sx = w->sx;
! 470: sy = ((height + 1) * rows) - 1;
! 471: if (sy < w->sy)
! 472: sy = w->sy;
! 473: layout_set_size(lc, sx, sy, 0, 0);
1.6 nicm 474: layout_make_node(lc, LAYOUT_TOPBOTTOM);
475:
476: /* Create a grid of the cells. */
477: wp = TAILQ_FIRST(&w->panes);
478: for (j = 0; j < rows; j++) {
479: /* If this is the last cell, all done. */
480: if (wp == NULL)
481: break;
482:
483: /* Create the new row. */
484: lcrow = layout_create_cell(lc);
1.7 nicm 485: layout_set_size(lcrow, w->sx, height, 0, 0);
1.6 nicm 486: TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
487:
488: /* If only one column, just use the row directly. */
1.9 nicm 489: if (n - (j * columns) == 1 || columns == 1) {
1.6 nicm 490: layout_make_leaf(lcrow, wp);
491: wp = TAILQ_NEXT(wp, entry);
492: continue;
493: }
494:
495: /* Add in the columns. */
496: layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
497: for (i = 0; i < columns; i++) {
498: /* Create and add a pane cell. */
499: lcchild = layout_create_cell(lcrow);
1.7 nicm 500: layout_set_size(lcchild, width, height, 0, 0);
1.6 nicm 501: layout_make_leaf(lcchild, wp);
502: TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
503:
504: /* Move to the next cell. */
505: if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
506: break;
507: }
508:
509: /*
510: * Adjust the row and columns to fit the full width if
511: * necessary.
512: */
513: if (i == columns)
514: i--;
1.7 nicm 515: used = ((i + 1) * (width + 1)) - 1;
1.6 nicm 516: if (w->sx <= used)
517: continue;
518: lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
1.15 nicm 519: layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT,
520: w->sx - used);
1.6 nicm 521: }
522:
523: /* Adjust the last row height to fit if necessary. */
1.7 nicm 524: used = (rows * height) + rows - 1;
1.6 nicm 525: if (w->sy > used) {
526: lcrow = TAILQ_LAST(&lc->cells, layout_cells);
1.15 nicm 527: layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
528: w->sy - used);
1.1 nicm 529: }
530:
531: /* Fix cell offsets. */
532: layout_fix_offsets(lc);
1.21 nicm 533: layout_fix_panes(w);
1.1 nicm 534:
535: layout_print_cell(w->layout_root, __func__, 1);
536:
1.23 ! nicm 537: window_resize(w, lc->sx, lc->sy);
1.18 nicm 538: notify_window("window-layout-changed", w);
1.1 nicm 539: server_redraw_window(w);
540: }