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