Annotation of src/usr.bin/tmux/layout.c, Revision 1.43
1.43 ! nicm 1: /* $OpenBSD: layout.c,v 1.42 2019/04/17 14:44:33 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);
1.43 ! nicm 417: if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT)
1.28 nicm 418: layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1);
1.43 ! nicm 419: else if (lcother != NULL)
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.42 nicm 837: u_int sx, sy, xoff, yoff, size1, size2, minimum;
1.29 nicm 838: u_int new_size, saved_size, resize_first = 0;
1.42 nicm 839: int full_size = (flags & SPAWN_FULLSIZE), status;
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.42 nicm 849: status = options_get_number(wp->window->options, "pane-border-status");
1.1 nicm 850:
1.3 nicm 851: /* Copy the old cell size. */
852: sx = lc->sx;
853: sy = lc->sy;
854: xoff = lc->xoff;
855: yoff = lc->yoff;
856:
857: /* Check there is enough space for the two new panes. */
858: switch (type) {
859: case LAYOUT_LEFTRIGHT:
860: if (sx < PANE_MINIMUM * 2 + 1)
1.5 nicm 861: return (NULL);
1.3 nicm 862: break;
863: case LAYOUT_TOPBOTTOM:
1.42 nicm 864: minimum = PANE_MINIMUM * 2 + 1;
865: if (status != 0)
866: minimum += layout_need_status(lc, status == 1);
867: if (sy < minimum)
1.5 nicm 868: return (NULL);
1.3 nicm 869: break;
870: default:
871: fatalx("bad layout type");
872: }
1.4 nicm 873:
1.29 nicm 874: /*
875: * Calculate new cell sizes. size is the target size or -1 for middle
876: * split, size1 is the size of the top/left and size2 the bottom/right.
877: */
878: if (type == LAYOUT_LEFTRIGHT)
879: saved_size = sx;
880: else
881: saved_size = sy;
882: if (size < 0)
883: size2 = ((saved_size + 1) / 2) - 1;
1.40 nicm 884: else if (flags & SPAWN_BEFORE)
1.29 nicm 885: size2 = saved_size - size - 1;
886: else
887: size2 = size;
888: if (size2 < PANE_MINIMUM)
889: size2 = PANE_MINIMUM;
890: else if (size2 > saved_size - 2)
891: size2 = saved_size - 2;
892: size1 = saved_size - 1 - size2;
893:
894: /* Which size are we using? */
1.40 nicm 895: if (flags & SPAWN_BEFORE)
1.29 nicm 896: new_size = size2;
897: else
898: new_size = size1;
899:
900: /* Confirm there is enough space for full size pane. */
901: if (full_size && !layout_set_size_check(wp->window, lc, type, new_size))
902: return (NULL);
903:
1.3 nicm 904: if (lc->parent != NULL && lc->parent->type == type) {
905: /*
906: * If the parent exists and is of the same type as the split,
907: * create a new cell and insert it after this one.
908: */
1.9 nicm 909: lcparent = lc->parent;
910: lcnew = layout_create_cell(lcparent);
1.40 nicm 911: if (flags & SPAWN_BEFORE)
1.9 nicm 912: TAILQ_INSERT_BEFORE(lc, lcnew, entry);
913: else
914: TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
1.29 nicm 915: } else if (full_size && lc->parent == NULL && lc->type == type) {
916: /*
917: * If the new full size pane is the same type as the root
918: * split, insert the new pane under the existing root cell
919: * instead of creating a new root cell. The existing layout
920: * must be resized before inserting the new cell.
921: */
922: if (lc->type == LAYOUT_LEFTRIGHT) {
923: lc->sx = new_size;
924: layout_resize_child_cells(wp->window, lc);
925: lc->sx = saved_size;
926: } else if (lc->type == LAYOUT_TOPBOTTOM) {
927: lc->sy = new_size;
928: layout_resize_child_cells(wp->window, lc);
929: lc->sy = saved_size;
930: }
931: resize_first = 1;
932:
933: /* Create the new cell. */
934: lcnew = layout_create_cell(lc);
1.32 nicm 935: size = saved_size - 1 - new_size;
1.29 nicm 936: if (lc->type == LAYOUT_LEFTRIGHT)
1.32 nicm 937: layout_set_size(lcnew, size, sy, 0, 0);
1.29 nicm 938: else if (lc->type == LAYOUT_TOPBOTTOM)
1.32 nicm 939: layout_set_size(lcnew, sx, size, 0, 0);
1.40 nicm 940: if (flags & SPAWN_BEFORE)
1.29 nicm 941: TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry);
942: else
943: TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
1.3 nicm 944: } else {
945: /*
946: * Otherwise create a new parent and insert it.
947: */
1.4 nicm 948:
1.3 nicm 949: /* Create and insert the replacement parent. */
950: lcparent = layout_create_cell(lc->parent);
951: layout_make_node(lcparent, type);
952: layout_set_size(lcparent, sx, sy, xoff, yoff);
953: if (lc->parent == NULL)
954: wp->window->layout_root = lcparent;
955: else
956: TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
1.4 nicm 957:
1.3 nicm 958: /* Insert the old cell. */
959: lc->parent = lcparent;
960: TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
1.4 nicm 961:
1.3 nicm 962: /* Create the new child cell. */
963: lcnew = layout_create_cell(lcparent);
1.40 nicm 964: if (flags & SPAWN_BEFORE)
1.9 nicm 965: TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry);
966: else
967: TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
968: }
1.40 nicm 969: if (flags & SPAWN_BEFORE) {
1.9 nicm 970: lc1 = lcnew;
971: lc2 = lc;
972: } else {
973: lc1 = lc;
974: lc2 = lcnew;
1.3 nicm 975: }
976:
1.29 nicm 977: /*
978: * Set new cell sizes. size1 is the size of the top/left and size2 the
979: * bottom/right.
1.3 nicm 980: */
1.29 nicm 981: if (!resize_first && type == LAYOUT_LEFTRIGHT) {
1.9 nicm 982: layout_set_size(lc1, size1, sy, xoff, yoff);
983: layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff);
1.29 nicm 984: } else if (!resize_first && type == LAYOUT_TOPBOTTOM) {
1.9 nicm 985: layout_set_size(lc1, sx, size1, xoff, yoff);
986: layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1);
1.3 nicm 987: }
1.29 nicm 988: if (full_size) {
989: if (!resize_first)
990: layout_resize_child_cells(wp->window, lc);
991: layout_fix_offsets(wp->window->layout_root);
992: } else
993: layout_make_leaf(lc, wp);
1.1 nicm 994:
1.5 nicm 995: return (lcnew);
1.3 nicm 996: }
1.1 nicm 997:
1.6 nicm 998: /* Destroy the cell associated with a pane. */
1.3 nicm 999: void
1000: layout_close_pane(struct window_pane *wp)
1001: {
1.28 nicm 1002: struct window *w = wp->window;
1003:
1.6 nicm 1004: /* Remove the cell. */
1.28 nicm 1005: layout_destroy_cell(w, wp->layout_cell, &w->layout_root);
1.1 nicm 1006:
1.6 nicm 1007: /* Fix pane offsets and sizes. */
1.28 nicm 1008: if (w->layout_root != NULL) {
1009: layout_fix_offsets(w->layout_root);
1.38 nicm 1010: layout_fix_panes(w);
1.3 nicm 1011: }
1.31 nicm 1012: notify_window("window-layout-changed", w);
1.33 nicm 1013: }
1014:
1015: int
1016: layout_spread_cell(struct window *w, struct layout_cell *parent)
1017: {
1018: struct layout_cell *lc;
1.42 nicm 1019: u_int number, each, size, this;
1020: int change, changed, status;
1.33 nicm 1021:
1022: number = 0;
1023: TAILQ_FOREACH (lc, &parent->cells, entry)
1024: number++;
1025: if (number <= 1)
1026: return (0);
1.42 nicm 1027: status = options_get_number(w->options, "pane-border-status");
1.33 nicm 1028:
1029: if (parent->type == LAYOUT_LEFTRIGHT)
1030: size = parent->sx;
1.42 nicm 1031: else if (parent->type == LAYOUT_TOPBOTTOM) {
1.33 nicm 1032: size = parent->sy;
1.42 nicm 1033: if (status != 0)
1034: size -= layout_need_status(parent, status == 1);
1035: } else
1036: return (0);
1037: if (size < number - 1)
1.33 nicm 1038: return (0);
1039: each = (size - (number - 1)) / number;
1.42 nicm 1040: if (each == 0)
1041: return (0);
1.33 nicm 1042:
1043: changed = 0;
1044: TAILQ_FOREACH (lc, &parent->cells, entry) {
1045: if (TAILQ_NEXT(lc, entry) == NULL)
1.35 nicm 1046: each = size - ((each + 1) * (number - 1));
1.33 nicm 1047: change = 0;
1048: if (parent->type == LAYOUT_LEFTRIGHT) {
1049: change = each - (int)lc->sx;
1050: layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
1051: } else if (parent->type == LAYOUT_TOPBOTTOM) {
1.42 nicm 1052: this = each;
1053: if (status != 0)
1054: this += layout_need_status(lc, status == 1);
1055: change = this - (int)lc->sy;
1.33 nicm 1056: layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
1057: }
1058: if (change != 0)
1059: changed = 1;
1060: }
1061: return (changed);
1062: }
1063:
1064: void
1065: layout_spread_out(struct window_pane *wp)
1066: {
1067: struct layout_cell *parent;
1068: struct window *w = wp->window;
1069:
1070: parent = wp->layout_cell->parent;
1071: if (parent == NULL)
1072: return;
1073:
1074: do {
1075: if (layout_spread_cell(w, parent)) {
1076: layout_fix_offsets(parent);
1.38 nicm 1077: layout_fix_panes(w);
1.33 nicm 1078: break;
1079: }
1080: } while ((parent = parent->parent) != NULL);
1.1 nicm 1081: }