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