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