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