Annotation of src/usr.bin/tmux/layout.c, Revision 1.48
1.48 ! nicm 1: /* $OpenBSD: layout.c,v 1.47 2019/07/15 18:43:32 nicm Exp $ */
1.1 nicm 2:
3: /*
1.26 nicm 4: * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
1.29 nicm 5: * Copyright (c) 2016 Stephen Kent <smkent@smkent.net>
1.1 nicm 6: *
7: * Permission to use, copy, modify, and distribute this software for any
8: * purpose with or without fee is hereby granted, provided that the above
9: * copyright notice and this permission notice appear in all copies.
10: *
11: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18: */
19:
20: #include <sys/types.h>
21:
1.3 nicm 22: #include <stdlib.h>
1.1 nicm 23:
24: #include "tmux.h"
25:
26: /*
1.3 nicm 27: * The window layout is a tree of cells each of which can be one of: a
28: * left-right container for a list of cells, a top-bottom container for a list
29: * of cells, or a container for a window pane.
1.1 nicm 30: *
1.3 nicm 31: * Each window has a pointer to the root of its layout tree (containing its
32: * panes), every pane has a pointer back to the cell containing it, and each
33: * cell a pointer to its parent cell.
1.1 nicm 34: */
35:
1.28 nicm 36: static u_int layout_resize_check(struct window *, struct layout_cell *,
37: enum layout_type);
38: static int layout_resize_pane_grow(struct window *, struct layout_cell *,
1.30 nicm 39: enum layout_type, int, int);
1.28 nicm 40: static int layout_resize_pane_shrink(struct window *, struct layout_cell *,
1.27 nicm 41: enum layout_type, int);
1.29 nicm 42: static u_int layout_new_pane_size(struct window *, u_int,
43: struct layout_cell *, enum layout_type, u_int, u_int,
44: u_int);
45: static int layout_set_size_check(struct window *, struct layout_cell *,
46: enum layout_type, int);
47: static void layout_resize_child_cells(struct window *,
48: struct layout_cell *);
1.1 nicm 49:
1.3 nicm 50: struct layout_cell *
51: layout_create_cell(struct layout_cell *lcparent)
1.1 nicm 52: {
1.3 nicm 53: struct layout_cell *lc;
54:
55: lc = xmalloc(sizeof *lc);
56: lc->type = LAYOUT_WINDOWPANE;
57: lc->parent = lcparent;
58:
59: TAILQ_INIT(&lc->cells);
1.4 nicm 60:
1.3 nicm 61: lc->sx = UINT_MAX;
62: lc->sy = UINT_MAX;
1.4 nicm 63:
1.3 nicm 64: lc->xoff = UINT_MAX;
65: lc->yoff = UINT_MAX;
1.4 nicm 66:
1.3 nicm 67: lc->wp = NULL;
68:
69: return (lc);
1.1 nicm 70: }
71:
1.3 nicm 72: void
73: layout_free_cell(struct layout_cell *lc)
1.1 nicm 74: {
1.3 nicm 75: struct layout_cell *lcchild;
1.1 nicm 76:
1.3 nicm 77: switch (lc->type) {
78: case LAYOUT_LEFTRIGHT:
79: case LAYOUT_TOPBOTTOM:
80: while (!TAILQ_EMPTY(&lc->cells)) {
81: lcchild = TAILQ_FIRST(&lc->cells);
82: TAILQ_REMOVE(&lc->cells, lcchild, entry);
83: layout_free_cell(lcchild);
1.1 nicm 84: }
1.3 nicm 85: break;
86: case LAYOUT_WINDOWPANE:
87: if (lc->wp != NULL)
88: lc->wp->layout_cell = NULL;
89: break;
1.1 nicm 90: }
91:
1.14 nicm 92: free(lc);
1.1 nicm 93: }
94:
1.3 nicm 95: void
96: layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
1.1 nicm 97: {
1.3 nicm 98: struct layout_cell *lcchild;
1.34 nicm 99: const char *type;
1.1 nicm 100:
1.34 nicm 101: switch (lc->type) {
102: case LAYOUT_LEFTRIGHT:
103: type = "LEFTRIGHT";
104: break;
105: case LAYOUT_TOPBOTTOM:
106: type = "TOPBOTTOM";
107: break;
108: case LAYOUT_WINDOWPANE:
109: type = "WINDOWPANE";
110: break;
111: default:
112: type = "UNKNOWN";
113: break;
114: }
115: log_debug("%s:%*s%p type %s [parent %p] wp=%p [%u,%u %ux%u]", hdr, n,
116: " ", lc, type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx,
1.25 nicm 117: lc->sy);
1.3 nicm 118: switch (lc->type) {
119: case LAYOUT_LEFTRIGHT:
120: case LAYOUT_TOPBOTTOM:
121: TAILQ_FOREACH(lcchild, &lc->cells, entry)
122: layout_print_cell(lcchild, hdr, n + 1);
123: break;
124: case LAYOUT_WINDOWPANE:
125: break;
126: }
1.1 nicm 127: }
128:
1.36 nicm 129: struct layout_cell *
130: layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
131: {
132: struct layout_cell *lcchild, *last = NULL;
133:
134: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
135: if (x >= lcchild->xoff && x < lcchild->xoff + lcchild->sx &&
136: y >= lcchild->yoff && y < lcchild->yoff + lcchild->sy) {
137: /* Inside the cell - recurse. */
138: return (layout_search_by_border(lcchild, x, y));
139: }
140:
141: if (last == NULL) {
142: last = lcchild;
143: continue;
144: }
145:
146: switch (lc->type) {
147: case LAYOUT_LEFTRIGHT:
148: if (x < lcchild->xoff && x >= last->xoff + last->sx)
149: return (last);
150: break;
151: case LAYOUT_TOPBOTTOM:
152: if (y < lcchild->yoff && y >= last->yoff + last->sy)
153: return (last);
154: break;
155: case LAYOUT_WINDOWPANE:
156: break;
157: }
158:
159: last = lcchild;
160: }
161:
162: return (NULL);
163: }
164:
1.1 nicm 165: void
1.25 nicm 166: layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff,
167: u_int yoff)
1.1 nicm 168: {
1.3 nicm 169: lc->sx = sx;
170: lc->sy = sy;
171:
172: lc->xoff = xoff;
173: lc->yoff = yoff;
1.1 nicm 174: }
175:
176: void
1.3 nicm 177: layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
1.1 nicm 178: {
1.3 nicm 179: lc->type = LAYOUT_WINDOWPANE;
180:
181: TAILQ_INIT(&lc->cells);
182:
183: wp->layout_cell = lc;
184: lc->wp = wp;
1.1 nicm 185: }
186:
187: void
1.3 nicm 188: layout_make_node(struct layout_cell *lc, enum layout_type type)
1.1 nicm 189: {
1.3 nicm 190: if (type == LAYOUT_WINDOWPANE)
191: fatalx("bad layout type");
192: lc->type = type;
193:
194: TAILQ_INIT(&lc->cells);
195:
196: if (lc->wp != NULL)
197: lc->wp->layout_cell = NULL;
198: lc->wp = NULL;
1.1 nicm 199: }
200:
1.47 nicm 201: /* Fix cell offsets for a child cell. */
1.46 nicm 202: static void
203: layout_fix_offsets1(struct layout_cell *lc)
1.1 nicm 204: {
1.3 nicm 205: struct layout_cell *lcchild;
206: u_int xoff, yoff;
1.1 nicm 207:
1.3 nicm 208: if (lc->type == LAYOUT_LEFTRIGHT) {
209: xoff = lc->xoff;
210: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
211: lcchild->xoff = xoff;
212: lcchild->yoff = lc->yoff;
213: if (lcchild->type != LAYOUT_WINDOWPANE)
1.46 nicm 214: layout_fix_offsets1(lcchild);
1.3 nicm 215: xoff += lcchild->sx + 1;
216: }
217: } else {
218: yoff = lc->yoff;
219: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
220: lcchild->xoff = lc->xoff;
221: lcchild->yoff = yoff;
222: if (lcchild->type != LAYOUT_WINDOWPANE)
1.46 nicm 223: layout_fix_offsets1(lcchild);
1.3 nicm 224: yoff += lcchild->sy + 1;
225: }
226: }
1.1 nicm 227: }
228:
1.46 nicm 229: /* Update cell offsets based on their sizes. */
230: void
231: layout_fix_offsets(struct window *w)
232: {
233: struct layout_cell *lc = w->layout_root;
234:
235: lc->xoff = 0;
236: lc->yoff = 0;
237:
238: layout_fix_offsets1(lc);
239: }
240:
1.47 nicm 241: /* Is this a top cell? */
1.27 nicm 242: static int
1.47 nicm 243: layout_cell_is_top(struct window *w, struct layout_cell *lc)
1.27 nicm 244: {
1.44 nicm 245: struct layout_cell *next;
1.27 nicm 246:
1.47 nicm 247: while (lc != w->layout_root) {
248: next = lc->parent;
249: if (next->type == LAYOUT_TOPBOTTOM &&
250: lc != TAILQ_FIRST(&next->cells))
251: return (0);
252: lc = next;
253: }
254: return (1);
255: }
1.27 nicm 256:
1.47 nicm 257: /* Is this a bottom cell? */
258: static int
259: layout_cell_is_bottom(struct window *w, struct layout_cell *lc)
260: {
261: struct layout_cell *next;
262:
263: while (lc != w->layout_root) {
264: next = lc->parent;
265: if (next->type == LAYOUT_TOPBOTTOM &&
266: lc != TAILQ_LAST(&next->cells, layout_cells))
267: return (0);
268: lc = next;
1.27 nicm 269: }
270: return (1);
271: }
272:
1.47 nicm 273: /*
274: * Returns 1 if we need to add an extra line for the pane status line. This is
275: * the case for the most upper or lower panes only.
276: */
277: static int
278: layout_add_border(struct window *w, struct layout_cell *lc, int status)
279: {
280: if (status == PANE_STATUS_TOP)
281: return (layout_cell_is_top(w, lc));
282: if (status == PANE_STATUS_BOTTOM)
283: return (layout_cell_is_bottom(w, lc));
284: return (0);
285: }
286:
1.3 nicm 287: /* Update pane offsets and sizes based on their cells. */
1.1 nicm 288: void
1.48 ! nicm 289: layout_fix_panes(struct window *w, struct window_pane *skip)
1.1 nicm 290: {
291: struct window_pane *wp;
1.3 nicm 292: struct layout_cell *lc;
1.47 nicm 293: int status;
1.1 nicm 294:
1.27 nicm 295: status = options_get_number(w->options, "pane-border-status");
1.1 nicm 296: TAILQ_FOREACH(wp, &w->panes, entry) {
1.48 ! nicm 297: if ((lc = wp->layout_cell) == NULL || wp == skip)
1.3 nicm 298: continue;
1.27 nicm 299:
1.3 nicm 300: wp->xoff = lc->xoff;
301: wp->yoff = lc->yoff;
302:
1.47 nicm 303: if (layout_add_border(w, lc, status)) {
304: if (status == PANE_STATUS_TOP)
305: wp->yoff++;
306: window_pane_resize(wp, lc->sx, lc->sy - 1);
307: } else
308: window_pane_resize(wp, lc->sx, lc->sy);
1.1 nicm 309: }
310: }
311:
1.6 nicm 312: /* Count the number of available cells in a layout. */
313: u_int
314: layout_count_cells(struct layout_cell *lc)
315: {
316: struct layout_cell *lcchild;
1.29 nicm 317: u_int count;
1.6 nicm 318:
319: switch (lc->type) {
320: case LAYOUT_WINDOWPANE:
321: return (1);
322: case LAYOUT_LEFTRIGHT:
323: case LAYOUT_TOPBOTTOM:
1.29 nicm 324: count = 0;
1.6 nicm 325: TAILQ_FOREACH(lcchild, &lc->cells, entry)
1.29 nicm 326: count += layout_count_cells(lcchild);
327: return (count);
1.6 nicm 328: default:
329: fatalx("bad layout type");
330: }
331: }
332:
1.3 nicm 333: /* Calculate how much size is available to be removed from a cell. */
1.28 nicm 334: static u_int
335: layout_resize_check(struct window *w, struct layout_cell *lc,
336: enum layout_type type)
1.1 nicm 337: {
1.3 nicm 338: struct layout_cell *lcchild;
339: u_int available, minimum;
1.37 nicm 340: int status;
1.1 nicm 341:
1.37 nicm 342: status = options_get_number(w->options, "pane-border-status");
1.3 nicm 343: if (lc->type == LAYOUT_WINDOWPANE) {
344: /* Space available in this cell only. */
1.47 nicm 345: if (type == LAYOUT_LEFTRIGHT) {
1.3 nicm 346: available = lc->sx;
1.47 nicm 347: minimum = PANE_MINIMUM;
348: } else {
1.3 nicm 349: available = lc->sy;
1.47 nicm 350: if (layout_add_border(w, lc, status))
351: minimum = PANE_MINIMUM + 1;
352: else
353: minimum = PANE_MINIMUM;
1.28 nicm 354: }
355: if (available > minimum)
356: available -= minimum;
1.3 nicm 357: else
358: available = 0;
359: } else if (lc->type == type) {
360: /* Same type: total of available space in all child cells. */
361: available = 0;
362: TAILQ_FOREACH(lcchild, &lc->cells, entry)
1.28 nicm 363: available += layout_resize_check(w, lcchild, type);
1.3 nicm 364: } else {
365: /* Different type: minimum of available space in child cells. */
366: minimum = UINT_MAX;
367: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
1.28 nicm 368: available = layout_resize_check(w, lcchild, type);
1.3 nicm 369: if (available < minimum)
370: minimum = available;
371: }
372: available = minimum;
1.2 nicm 373: }
374:
1.3 nicm 375: return (available);
376: }
1.1 nicm 377:
1.3 nicm 378: /*
379: * Adjust cell size evenly, including altering its children. This function
380: * expects the change to have already been bounded to the space available.
381: */
382: void
1.28 nicm 383: layout_resize_adjust(struct window *w, struct layout_cell *lc,
384: enum layout_type type, int change)
1.3 nicm 385: {
386: struct layout_cell *lcchild;
1.1 nicm 387:
1.3 nicm 388: /* Adjust the cell size. */
389: if (type == LAYOUT_LEFTRIGHT)
390: lc->sx += change;
391: else
392: lc->sy += change;
1.4 nicm 393:
1.3 nicm 394: /* If this is a leaf cell, that is all that is necessary. */
395: if (type == LAYOUT_WINDOWPANE)
396: return;
397:
398: /* Child cell runs in a different direction. */
399: if (lc->type != type) {
400: TAILQ_FOREACH(lcchild, &lc->cells, entry)
1.28 nicm 401: layout_resize_adjust(w, lcchild, type, change);
1.3 nicm 402: return;
1.1 nicm 403: }
404:
1.4 nicm 405: /*
406: * Child cell runs in the same direction. Adjust each child equally
1.3 nicm 407: * until no further change is possible.
408: */
409: while (change != 0) {
410: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
411: if (change == 0)
412: break;
413: if (change > 0) {
1.28 nicm 414: layout_resize_adjust(w, lcchild, type, 1);
1.3 nicm 415: change--;
416: continue;
417: }
1.28 nicm 418: if (layout_resize_check(w, lcchild, type) > 0) {
419: layout_resize_adjust(w, lcchild, type, -1);
1.3 nicm 420: change++;
421: }
422: }
1.1 nicm 423: }
424: }
425:
1.6 nicm 426: /* Destroy a cell and redistribute the space. */
427: void
1.28 nicm 428: layout_destroy_cell(struct window *w, struct layout_cell *lc,
429: struct layout_cell **lcroot)
1.6 nicm 430: {
431: struct layout_cell *lcother, *lcparent;
432:
433: /*
434: * If no parent, this is the last pane so window close is imminent and
435: * there is no need to resize anything.
436: */
437: lcparent = lc->parent;
438: if (lcparent == NULL) {
439: layout_free_cell(lc);
440: *lcroot = NULL;
441: return;
442: }
443:
444: /* Merge the space into the previous or next cell. */
445: if (lc == TAILQ_FIRST(&lcparent->cells))
446: lcother = TAILQ_NEXT(lc, entry);
447: else
448: lcother = TAILQ_PREV(lc, layout_cells, entry);
1.43 nicm 449: if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT)
1.28 nicm 450: layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1);
1.43 nicm 451: else if (lcother != NULL)
1.28 nicm 452: layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1);
1.6 nicm 453:
454: /* Remove this from the parent's list. */
455: TAILQ_REMOVE(&lcparent->cells, lc, entry);
456: layout_free_cell(lc);
457:
458: /*
459: * If the parent now has one cell, remove the parent from the tree and
460: * replace it by that cell.
461: */
462: lc = TAILQ_FIRST(&lcparent->cells);
463: if (TAILQ_NEXT(lc, entry) == NULL) {
464: TAILQ_REMOVE(&lcparent->cells, lc, entry);
465:
466: lc->parent = lcparent->parent;
467: if (lc->parent == NULL) {
468: lc->xoff = 0; lc->yoff = 0;
469: *lcroot = lc;
470: } else
471: TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
472:
473: layout_free_cell(lcparent);
474: }
475: }
476:
1.1 nicm 477: void
1.18 nicm 478: layout_init(struct window *w, struct window_pane *wp)
1.1 nicm 479: {
1.3 nicm 480: struct layout_cell *lc;
1.1 nicm 481:
1.3 nicm 482: lc = w->layout_root = layout_create_cell(NULL);
483: layout_set_size(lc, w->sx, w->sy, 0, 0);
1.18 nicm 484: layout_make_leaf(lc, wp);
1.48 ! nicm 485: layout_fix_panes(w, NULL);
1.3 nicm 486: }
1.2 nicm 487:
1.3 nicm 488: void
489: layout_free(struct window *w)
490: {
491: layout_free_cell(w->layout_root);
492: }
1.1 nicm 493:
1.3 nicm 494: /* Resize the entire layout after window resize. */
495: void
496: layout_resize(struct window *w, u_int sx, u_int sy)
497: {
498: struct layout_cell *lc = w->layout_root;
499: int xlimit, ylimit, xchange, ychange;
1.1 nicm 500:
1.4 nicm 501: /*
1.3 nicm 502: * Adjust horizontally. Do not attempt to reduce the layout lower than
503: * the minimum (more than the amount returned by layout_resize_check).
1.4 nicm 504: *
1.3 nicm 505: * This can mean that the window size is smaller than the total layout
506: * size: redrawing this is handled at a higher level, but it does leave
507: * a problem with growing the window size here: if the current size is
508: * < the minimum, growing proportionately by adding to each pane is
509: * wrong as it would keep the layout size larger than the window size.
510: * Instead, spread the difference between the minimum and the new size
511: * out proportionately - this should leave the layout fitting the new
512: * window size.
513: */
1.41 nicm 514: xchange = sx - lc->sx;
1.28 nicm 515: xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
1.3 nicm 516: if (xchange < 0 && xchange < -xlimit)
517: xchange = -xlimit;
518: if (xlimit == 0) {
519: if (sx <= lc->sx) /* lc->sx is minimum possible */
520: xchange = 0;
1.1 nicm 521: else
1.3 nicm 522: xchange = sx - lc->sx;
1.1 nicm 523: }
1.3 nicm 524: if (xchange != 0)
1.28 nicm 525: layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, xchange);
1.1 nicm 526:
1.3 nicm 527: /* Adjust vertically in a similar fashion. */
1.41 nicm 528: ychange = sy - lc->sy;
1.28 nicm 529: ylimit = layout_resize_check(w, lc, LAYOUT_TOPBOTTOM);
1.3 nicm 530: if (ychange < 0 && ychange < -ylimit)
531: ychange = -ylimit;
532: if (ylimit == 0) {
533: if (sy <= lc->sy) /* lc->sy is minimum possible */
534: ychange = 0;
535: else
536: ychange = sy - lc->sy;
1.1 nicm 537: }
1.3 nicm 538: if (ychange != 0)
1.28 nicm 539: layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange);
1.4 nicm 540:
1.3 nicm 541: /* Fix cell offsets. */
1.46 nicm 542: layout_fix_offsets(w);
1.48 ! nicm 543: layout_fix_panes(w, NULL);
1.1 nicm 544: }
545:
1.17 nicm 546: /* Resize a pane to an absolute size. */
547: void
548: layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
549: u_int new_size)
550: {
551: struct layout_cell *lc, *lcparent;
552: int change, size;
553:
554: lc = wp->layout_cell;
555:
556: /* Find next parent of the same type. */
557: lcparent = lc->parent;
558: while (lcparent != NULL && lcparent->type != type) {
559: lc = lcparent;
560: lcparent = lc->parent;
561: }
562: if (lcparent == NULL)
563: return;
564:
565: /* Work out the size adjustment. */
566: if (type == LAYOUT_LEFTRIGHT)
567: size = lc->sx;
568: else
569: size = lc->sy;
570: if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
571: change = size - new_size;
572: else
573: change = new_size - size;
574:
575: /* Resize the pane. */
1.30 nicm 576: layout_resize_pane(wp, type, change, 1);
1.17 nicm 577: }
578:
1.36 nicm 579: void
580: layout_resize_layout(struct window *w, struct layout_cell *lc,
581: enum layout_type type, int change, int opposite)
582: {
583: int needed, size;
584:
585: /* Grow or shrink the cell. */
586: needed = change;
587: while (needed != 0) {
588: if (change > 0) {
589: size = layout_resize_pane_grow(w, lc, type, needed,
590: opposite);
591: needed -= size;
592: } else {
593: size = layout_resize_pane_shrink(w, lc, type, needed);
594: needed += size;
595: }
596:
597: if (size == 0) /* no more change possible */
598: break;
599: }
600:
601: /* Fix cell offsets. */
1.46 nicm 602: layout_fix_offsets(w);
1.48 ! nicm 603: layout_fix_panes(w, NULL);
1.36 nicm 604: notify_window("window-layout-changed", w);
605: }
606:
1.3 nicm 607: /* Resize a single pane within the layout. */
1.1 nicm 608: void
1.30 nicm 609: layout_resize_pane(struct window_pane *wp, enum layout_type type, int change,
610: int opposite)
1.1 nicm 611: {
1.28 nicm 612: struct layout_cell *lc, *lcparent;
1.1 nicm 613:
1.3 nicm 614: lc = wp->layout_cell;
1.1 nicm 615:
1.3 nicm 616: /* Find next parent of the same type. */
617: lcparent = lc->parent;
618: while (lcparent != NULL && lcparent->type != type) {
619: lc = lcparent;
620: lcparent = lc->parent;
621: }
622: if (lcparent == NULL)
623: return;
624:
625: /* If this is the last cell, move back one. */
626: if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
627: lc = TAILQ_PREV(lc, layout_cells, entry);
628:
1.36 nicm 629: layout_resize_layout(wp->window, lc, type, change, opposite);
1.3 nicm 630: }
1.1 nicm 631:
1.17 nicm 632: /* Helper function to grow pane. */
1.27 nicm 633: static int
1.28 nicm 634: layout_resize_pane_grow(struct window *w, struct layout_cell *lc,
1.30 nicm 635: enum layout_type type, int needed, int opposite)
1.3 nicm 636: {
637: struct layout_cell *lcadd, *lcremove;
1.30 nicm 638: u_int size = 0;
1.3 nicm 639:
640: /* Growing. Always add to the current cell. */
641: lcadd = lc;
1.4 nicm 642:
1.3 nicm 643: /* Look towards the tail for a suitable cell for reduction. */
644: lcremove = TAILQ_NEXT(lc, entry);
645: while (lcremove != NULL) {
1.28 nicm 646: size = layout_resize_check(w, lcremove, type);
1.3 nicm 647: if (size > 0)
648: break;
1.4 nicm 649: lcremove = TAILQ_NEXT(lcremove, entry);
1.1 nicm 650: }
651:
1.3 nicm 652: /* If none found, look towards the head. */
1.30 nicm 653: if (opposite && lcremove == NULL) {
1.3 nicm 654: lcremove = TAILQ_PREV(lc, layout_cells, entry);
655: while (lcremove != NULL) {
1.28 nicm 656: size = layout_resize_check(w, lcremove, type);
1.3 nicm 657: if (size > 0)
658: break;
659: lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
1.1 nicm 660: }
1.3 nicm 661: }
1.30 nicm 662: if (lcremove == NULL)
663: return (0);
1.1 nicm 664:
1.3 nicm 665: /* Change the cells. */
666: if (size > (u_int) needed)
667: size = needed;
1.28 nicm 668: layout_resize_adjust(w, lcadd, type, size);
669: layout_resize_adjust(w, lcremove, type, -size);
1.3 nicm 670: return (size);
671: }
1.1 nicm 672:
1.17 nicm 673: /* Helper function to shrink pane. */
1.27 nicm 674: static int
1.28 nicm 675: layout_resize_pane_shrink(struct window *w, struct layout_cell *lc,
676: enum layout_type type, int needed)
1.3 nicm 677: {
678: struct layout_cell *lcadd, *lcremove;
679: u_int size;
1.1 nicm 680:
1.3 nicm 681: /* Shrinking. Find cell to remove from by walking towards head. */
682: lcremove = lc;
683: do {
1.28 nicm 684: size = layout_resize_check(w, lcremove, type);
1.3 nicm 685: if (size != 0)
1.1 nicm 686: break;
1.3 nicm 687: lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
688: } while (lcremove != NULL);
689: if (lcremove == NULL)
690: return (0);
691:
692: /* And add onto the next cell (from the original cell). */
693: lcadd = TAILQ_NEXT(lc, entry);
694: if (lcadd == NULL)
695: return (0);
696:
697: /* Change the cells. */
698: if (size > (u_int) -needed)
699: size = -needed;
1.28 nicm 700: layout_resize_adjust(w, lcadd, type, size);
701: layout_resize_adjust(w, lcremove, type, -size);
1.3 nicm 702: return (size);
1.1 nicm 703: }
704:
1.5 nicm 705: /* Assign window pane to newly split cell. */
706: void
1.48 ! nicm 707: layout_assign_pane(struct layout_cell *lc, struct window_pane *wp,
! 708: int do_not_resize)
1.5 nicm 709: {
710: layout_make_leaf(lc, wp);
1.48 ! nicm 711: if (do_not_resize)
! 712: layout_fix_panes(wp->window, wp);
! 713: else
! 714: layout_fix_panes(wp->window, NULL);
1.5 nicm 715: }
716:
1.29 nicm 717: /* Calculate the new pane size for resized parent. */
718: static u_int
719: layout_new_pane_size(struct window *w, u_int previous, struct layout_cell *lc,
720: enum layout_type type, u_int size, u_int count_left, u_int size_left)
721: {
722: u_int new_size, min, max, available;
723:
724: /* If this is the last cell, it can take all of the remaining size. */
725: if (count_left == 1)
726: return (size_left);
727:
728: /* How much is available in this parent? */
729: available = layout_resize_check(w, lc, type);
730:
731: /*
732: * Work out the minimum size of this cell and the new size
733: * proportionate to the previous size.
734: */
735: min = (PANE_MINIMUM + 1) * (count_left - 1);
736: if (type == LAYOUT_LEFTRIGHT) {
737: if (lc->sx - available > min)
738: min = lc->sx - available;
739: new_size = (lc->sx * size) / previous;
740: } else {
741: if (lc->sy - available > min)
742: min = lc->sy - available;
743: new_size = (lc->sy * size) / previous;
744: }
745:
746: /* Check against the maximum and minimum size. */
747: max = size_left - min;
748: if (new_size > max)
749: new_size = max;
750: if (new_size < PANE_MINIMUM)
751: new_size = PANE_MINIMUM;
752: return (new_size);
753: }
754:
755: /* Check if the cell and all its children can be resized to a specific size. */
756: static int
757: layout_set_size_check(struct window *w, struct layout_cell *lc,
758: enum layout_type type, int size)
759: {
760: struct layout_cell *lcchild;
1.39 nicm 761: u_int new_size, available, previous, count, idx;
1.29 nicm 762:
763: /* Cells with no children must just be bigger than minimum. */
764: if (lc->type == LAYOUT_WINDOWPANE)
765: return (size >= PANE_MINIMUM);
766: available = size;
767:
768: /* Count number of children. */
769: count = 0;
770: TAILQ_FOREACH(lcchild, &lc->cells, entry)
771: count++;
772:
773: /* Check new size will work for each child. */
774: if (lc->type == type) {
1.39 nicm 775: if (available < (count * 2) - 1)
776: return (0);
777:
1.29 nicm 778: if (type == LAYOUT_LEFTRIGHT)
779: previous = lc->sx;
780: else
781: previous = lc->sy;
782:
783: idx = 0;
784: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
785: new_size = layout_new_pane_size(w, previous, lcchild,
786: type, size, count - idx, available);
1.39 nicm 787: if (idx == count - 1) {
788: if (new_size > available)
789: return (0);
790: available -= new_size;
791: } else {
792: if (new_size + 1 > available)
793: return (0);
794: available -= new_size + 1;
795: }
1.29 nicm 796: if (!layout_set_size_check(w, lcchild, type, new_size))
797: return (0);
798: idx++;
799: }
800: } else {
801: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
802: if (lcchild->type == LAYOUT_WINDOWPANE)
803: continue;
804: if (!layout_set_size_check(w, lcchild, type, size))
805: return (0);
806: }
807: }
808:
809: return (1);
810: }
811:
812: /* Resize all child cells to fit within the current cell. */
813: static void
814: layout_resize_child_cells(struct window *w, struct layout_cell *lc)
815: {
816: struct layout_cell *lcchild;
817: u_int previous, available, count, idx;
818:
819: if (lc->type == LAYOUT_WINDOWPANE)
820: return;
821:
822: /* What is the current size used? */
823: count = 0;
824: previous = 0;
825: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
826: count++;
827: if (lc->type == LAYOUT_LEFTRIGHT)
828: previous += lcchild->sx;
829: else if (lc->type == LAYOUT_TOPBOTTOM)
830: previous += lcchild->sy;
831: }
832: previous += (count - 1);
833:
834: /* And how much is available? */
835: available = 0;
836: if (lc->type == LAYOUT_LEFTRIGHT)
837: available = lc->sx;
838: else if (lc->type == LAYOUT_TOPBOTTOM)
839: available = lc->sy;
840:
841: /* Resize children into the new size. */
842: idx = 0;
843: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
844: if (lc->type == LAYOUT_TOPBOTTOM) {
845: lcchild->sx = lc->sx;
846: lcchild->xoff = lc->xoff;
847: } else {
848: lcchild->sx = layout_new_pane_size(w, previous, lcchild,
849: lc->type, lc->sx, count - idx, available);
850: available -= (lcchild->sx + 1);
851: }
852: if (lc->type == LAYOUT_LEFTRIGHT)
853: lcchild->sy = lc->sy;
854: else {
855: lcchild->sy = layout_new_pane_size(w, previous, lcchild,
856: lc->type, lc->sy, count - idx, available);
857: available -= (lcchild->sy + 1);
858: }
859: layout_resize_child_cells(w, lcchild);
860: idx++;
861: }
862: }
863:
1.5 nicm 864: /*
865: * Split a pane into two. size is a hint, or -1 for default half/half
866: * split. This must be followed by layout_assign_pane before much else happens!
1.29 nicm 867: */
1.5 nicm 868: struct layout_cell *
1.25 nicm 869: layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
1.40 nicm 870: int flags)
1.1 nicm 871: {
1.9 nicm 872: struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
1.42 nicm 873: u_int sx, sy, xoff, yoff, size1, size2, minimum;
1.29 nicm 874: u_int new_size, saved_size, resize_first = 0;
1.42 nicm 875: int full_size = (flags & SPAWN_FULLSIZE), status;
1.1 nicm 876:
1.29 nicm 877: /*
878: * If full_size is specified, add a new cell at the top of the window
879: * layout. Otherwise, split the cell for the current pane.
880: */
881: if (full_size)
882: lc = wp->window->layout_root;
883: else
884: lc = wp->layout_cell;
1.42 nicm 885: status = options_get_number(wp->window->options, "pane-border-status");
1.1 nicm 886:
1.3 nicm 887: /* Copy the old cell size. */
888: sx = lc->sx;
889: sy = lc->sy;
890: xoff = lc->xoff;
891: yoff = lc->yoff;
892:
893: /* Check there is enough space for the two new panes. */
894: switch (type) {
895: case LAYOUT_LEFTRIGHT:
896: if (sx < PANE_MINIMUM * 2 + 1)
1.5 nicm 897: return (NULL);
1.3 nicm 898: break;
899: case LAYOUT_TOPBOTTOM:
1.47 nicm 900: if (layout_add_border(wp->window, lc, status))
901: minimum = PANE_MINIMUM * 2 + 2;
902: else
903: minimum = PANE_MINIMUM * 2 + 1;
1.42 nicm 904: if (sy < minimum)
1.5 nicm 905: return (NULL);
1.3 nicm 906: break;
907: default:
908: fatalx("bad layout type");
909: }
1.4 nicm 910:
1.29 nicm 911: /*
912: * Calculate new cell sizes. size is the target size or -1 for middle
913: * split, size1 is the size of the top/left and size2 the bottom/right.
914: */
915: if (type == LAYOUT_LEFTRIGHT)
916: saved_size = sx;
917: else
918: saved_size = sy;
919: if (size < 0)
920: size2 = ((saved_size + 1) / 2) - 1;
1.40 nicm 921: else if (flags & SPAWN_BEFORE)
1.29 nicm 922: size2 = saved_size - size - 1;
923: else
924: size2 = size;
925: if (size2 < PANE_MINIMUM)
926: size2 = PANE_MINIMUM;
927: else if (size2 > saved_size - 2)
928: size2 = saved_size - 2;
929: size1 = saved_size - 1 - size2;
930:
931: /* Which size are we using? */
1.40 nicm 932: if (flags & SPAWN_BEFORE)
1.29 nicm 933: new_size = size2;
934: else
935: new_size = size1;
936:
937: /* Confirm there is enough space for full size pane. */
938: if (full_size && !layout_set_size_check(wp->window, lc, type, new_size))
939: return (NULL);
940:
1.3 nicm 941: if (lc->parent != NULL && lc->parent->type == type) {
942: /*
943: * If the parent exists and is of the same type as the split,
944: * create a new cell and insert it after this one.
945: */
1.9 nicm 946: lcparent = lc->parent;
947: lcnew = layout_create_cell(lcparent);
1.40 nicm 948: if (flags & SPAWN_BEFORE)
1.9 nicm 949: TAILQ_INSERT_BEFORE(lc, lcnew, entry);
950: else
951: TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
1.29 nicm 952: } else if (full_size && lc->parent == NULL && lc->type == type) {
953: /*
954: * If the new full size pane is the same type as the root
955: * split, insert the new pane under the existing root cell
956: * instead of creating a new root cell. The existing layout
957: * must be resized before inserting the new cell.
958: */
959: if (lc->type == LAYOUT_LEFTRIGHT) {
960: lc->sx = new_size;
961: layout_resize_child_cells(wp->window, lc);
962: lc->sx = saved_size;
963: } else if (lc->type == LAYOUT_TOPBOTTOM) {
964: lc->sy = new_size;
965: layout_resize_child_cells(wp->window, lc);
966: lc->sy = saved_size;
967: }
968: resize_first = 1;
969:
970: /* Create the new cell. */
971: lcnew = layout_create_cell(lc);
1.32 nicm 972: size = saved_size - 1 - new_size;
1.29 nicm 973: if (lc->type == LAYOUT_LEFTRIGHT)
1.32 nicm 974: layout_set_size(lcnew, size, sy, 0, 0);
1.29 nicm 975: else if (lc->type == LAYOUT_TOPBOTTOM)
1.32 nicm 976: layout_set_size(lcnew, sx, size, 0, 0);
1.40 nicm 977: if (flags & SPAWN_BEFORE)
1.29 nicm 978: TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry);
979: else
980: TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
1.3 nicm 981: } else {
982: /*
983: * Otherwise create a new parent and insert it.
984: */
1.4 nicm 985:
1.3 nicm 986: /* Create and insert the replacement parent. */
987: lcparent = layout_create_cell(lc->parent);
988: layout_make_node(lcparent, type);
989: layout_set_size(lcparent, sx, sy, xoff, yoff);
990: if (lc->parent == NULL)
991: wp->window->layout_root = lcparent;
992: else
993: TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
1.4 nicm 994:
1.3 nicm 995: /* Insert the old cell. */
996: lc->parent = lcparent;
997: TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
1.4 nicm 998:
1.3 nicm 999: /* Create the new child cell. */
1000: lcnew = layout_create_cell(lcparent);
1.40 nicm 1001: if (flags & SPAWN_BEFORE)
1.9 nicm 1002: TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry);
1003: else
1004: TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
1005: }
1.40 nicm 1006: if (flags & SPAWN_BEFORE) {
1.9 nicm 1007: lc1 = lcnew;
1008: lc2 = lc;
1009: } else {
1010: lc1 = lc;
1011: lc2 = lcnew;
1.3 nicm 1012: }
1013:
1.29 nicm 1014: /*
1015: * Set new cell sizes. size1 is the size of the top/left and size2 the
1016: * bottom/right.
1.3 nicm 1017: */
1.29 nicm 1018: if (!resize_first && type == LAYOUT_LEFTRIGHT) {
1.9 nicm 1019: layout_set_size(lc1, size1, sy, xoff, yoff);
1020: layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff);
1.29 nicm 1021: } else if (!resize_first && type == LAYOUT_TOPBOTTOM) {
1.9 nicm 1022: layout_set_size(lc1, sx, size1, xoff, yoff);
1023: layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1);
1.3 nicm 1024: }
1.29 nicm 1025: if (full_size) {
1026: if (!resize_first)
1027: layout_resize_child_cells(wp->window, lc);
1.46 nicm 1028: layout_fix_offsets(wp->window);
1.29 nicm 1029: } else
1030: layout_make_leaf(lc, wp);
1.1 nicm 1031:
1.5 nicm 1032: return (lcnew);
1.3 nicm 1033: }
1.1 nicm 1034:
1.6 nicm 1035: /* Destroy the cell associated with a pane. */
1.3 nicm 1036: void
1037: layout_close_pane(struct window_pane *wp)
1038: {
1.28 nicm 1039: struct window *w = wp->window;
1040:
1.6 nicm 1041: /* Remove the cell. */
1.28 nicm 1042: layout_destroy_cell(w, wp->layout_cell, &w->layout_root);
1.1 nicm 1043:
1.6 nicm 1044: /* Fix pane offsets and sizes. */
1.28 nicm 1045: if (w->layout_root != NULL) {
1.46 nicm 1046: layout_fix_offsets(w);
1.48 ! nicm 1047: layout_fix_panes(w, NULL);
1.3 nicm 1048: }
1.31 nicm 1049: notify_window("window-layout-changed", w);
1.33 nicm 1050: }
1051:
1052: int
1053: layout_spread_cell(struct window *w, struct layout_cell *parent)
1054: {
1055: struct layout_cell *lc;
1.42 nicm 1056: u_int number, each, size, this;
1057: int change, changed, status;
1.33 nicm 1058:
1059: number = 0;
1060: TAILQ_FOREACH (lc, &parent->cells, entry)
1.45 nicm 1061: number++;
1.33 nicm 1062: if (number <= 1)
1063: return (0);
1.42 nicm 1064: status = options_get_number(w->options, "pane-border-status");
1.33 nicm 1065:
1066: if (parent->type == LAYOUT_LEFTRIGHT)
1067: size = parent->sx;
1.42 nicm 1068: else if (parent->type == LAYOUT_TOPBOTTOM) {
1.47 nicm 1069: if (layout_add_border(w, parent, status))
1070: size = parent->sy - 1;
1071: else
1072: size = parent->sy;
1.42 nicm 1073: } else
1074: return (0);
1075: if (size < number - 1)
1.33 nicm 1076: return (0);
1077: each = (size - (number - 1)) / number;
1.42 nicm 1078: if (each == 0)
1079: return (0);
1.33 nicm 1080:
1081: changed = 0;
1082: TAILQ_FOREACH (lc, &parent->cells, entry) {
1083: if (TAILQ_NEXT(lc, entry) == NULL)
1.35 nicm 1084: each = size - ((each + 1) * (number - 1));
1.33 nicm 1085: change = 0;
1086: if (parent->type == LAYOUT_LEFTRIGHT) {
1087: change = each - (int)lc->sx;
1088: layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
1089: } else if (parent->type == LAYOUT_TOPBOTTOM) {
1.47 nicm 1090: if (layout_add_border(w, lc, status))
1091: this = each + 1;
1092: else
1093: this = each;
1.42 nicm 1094: change = this - (int)lc->sy;
1.33 nicm 1095: layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
1096: }
1097: if (change != 0)
1098: changed = 1;
1099: }
1100: return (changed);
1101: }
1102:
1103: void
1104: layout_spread_out(struct window_pane *wp)
1105: {
1106: struct layout_cell *parent;
1107: struct window *w = wp->window;
1108:
1109: parent = wp->layout_cell->parent;
1110: if (parent == NULL)
1111: return;
1112:
1113: do {
1114: if (layout_spread_cell(w, parent)) {
1.46 nicm 1115: layout_fix_offsets(w);
1.48 ! nicm 1116: layout_fix_panes(w, NULL);
1.33 nicm 1117: break;
1118: }
1119: } while ((parent = parent->parent) != NULL);
1.1 nicm 1120: }