Annotation of src/usr.bin/tmux/layout.c, Revision 1.37
1.37 ! nicm 1: /* $OpenBSD: layout.c,v 1.36 2018/06/08 20:54:22 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.3 nicm 256: layout_fix_panes(struct window *w, u_int wsx, u_int wsy)
1.1 nicm 257: {
258: struct window_pane *wp;
1.3 nicm 259: struct layout_cell *lc;
260: u_int sx, sy;
1.27 nicm 261: int shift, status, at_top;
1.1 nicm 262:
1.27 nicm 263: status = options_get_number(w->options, "pane-border-status");
264: at_top = (status == 1);
1.1 nicm 265: TAILQ_FOREACH(wp, &w->panes, entry) {
1.3 nicm 266: if ((lc = wp->layout_cell) == NULL)
267: continue;
1.27 nicm 268:
269: if (status != 0)
270: shift = layout_need_status(lc, at_top);
271: else
272: shift = 0;
273:
1.3 nicm 274: wp->xoff = lc->xoff;
275: wp->yoff = lc->yoff;
276:
1.27 nicm 277: if (shift && at_top)
278: wp->yoff += 1;
279:
1.3 nicm 280: /*
281: * Layout cells are limited by the smallest size of other cells
282: * within the same row or column; if this isn't the case
283: * resizing becomes difficult.
284: *
285: * However, panes do not have to take up their entire cell, so
286: * they can be cropped to the window edge if the layout
287: * overflows and they are partly visible.
288: *
289: * This stops cells being hidden unnecessarily.
290: */
291:
292: /*
293: * Work out the horizontal size. If the pane is actually
294: * outside the window or the entire pane is already visible,
295: * don't crop.
296: */
297: if (lc->xoff >= wsx || lc->xoff + lc->sx < wsx)
298: sx = lc->sx;
299: else {
300: sx = wsx - lc->xoff;
301: if (sx < 1)
302: sx = lc->sx;
303: }
1.4 nicm 304:
305: /*
1.3 nicm 306: * Similarly for the vertical size; the minimum vertical size
307: * is two because scroll regions cannot be one line.
308: */
309: if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy)
310: sy = lc->sy;
1.2 nicm 311: else {
1.3 nicm 312: sy = wsy - lc->yoff;
313: if (sy < 2)
314: sy = lc->sy;
1.2 nicm 315: }
1.3 nicm 316:
1.27 nicm 317: if (shift)
318: sy -= 1;
319:
1.3 nicm 320: window_pane_resize(wp, sx, sy);
1.1 nicm 321: }
322: }
323:
1.6 nicm 324: /* Count the number of available cells in a layout. */
325: u_int
326: layout_count_cells(struct layout_cell *lc)
327: {
328: struct layout_cell *lcchild;
1.29 nicm 329: u_int count;
1.6 nicm 330:
331: switch (lc->type) {
332: case LAYOUT_WINDOWPANE:
333: return (1);
334: case LAYOUT_LEFTRIGHT:
335: case LAYOUT_TOPBOTTOM:
1.29 nicm 336: count = 0;
1.6 nicm 337: TAILQ_FOREACH(lcchild, &lc->cells, entry)
1.29 nicm 338: count += layout_count_cells(lcchild);
339: return (count);
1.6 nicm 340: default:
341: fatalx("bad layout type");
342: }
343: }
344:
1.3 nicm 345: /* Calculate how much size is available to be removed from a cell. */
1.28 nicm 346: static u_int
347: layout_resize_check(struct window *w, struct layout_cell *lc,
348: enum layout_type type)
1.1 nicm 349: {
1.3 nicm 350: struct layout_cell *lcchild;
351: u_int available, minimum;
1.37 ! nicm 352: int status;
1.1 nicm 353:
1.37 ! nicm 354: status = options_get_number(w->options, "pane-border-status");
1.3 nicm 355: if (lc->type == LAYOUT_WINDOWPANE) {
356: /* Space available in this cell only. */
1.28 nicm 357: minimum = PANE_MINIMUM;
1.3 nicm 358: if (type == LAYOUT_LEFTRIGHT)
359: available = lc->sx;
1.28 nicm 360: else {
1.3 nicm 361: available = lc->sy;
1.37 ! nicm 362: if (status != 0)
! 363: minimum += layout_need_status(lc, status == 1);
1.28 nicm 364: }
365: if (available > minimum)
366: available -= minimum;
1.3 nicm 367: else
368: available = 0;
369: } else if (lc->type == type) {
370: /* Same type: total of available space in all child cells. */
371: available = 0;
372: TAILQ_FOREACH(lcchild, &lc->cells, entry)
1.28 nicm 373: available += layout_resize_check(w, lcchild, type);
1.3 nicm 374: } else {
375: /* Different type: minimum of available space in child cells. */
376: minimum = UINT_MAX;
377: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
1.28 nicm 378: available = layout_resize_check(w, lcchild, type);
1.3 nicm 379: if (available < minimum)
380: minimum = available;
381: }
382: available = minimum;
1.2 nicm 383: }
384:
1.3 nicm 385: return (available);
386: }
1.1 nicm 387:
1.3 nicm 388: /*
389: * Adjust cell size evenly, including altering its children. This function
390: * expects the change to have already been bounded to the space available.
391: */
392: void
1.28 nicm 393: layout_resize_adjust(struct window *w, struct layout_cell *lc,
394: enum layout_type type, int change)
1.3 nicm 395: {
396: struct layout_cell *lcchild;
1.1 nicm 397:
1.3 nicm 398: /* Adjust the cell size. */
399: if (type == LAYOUT_LEFTRIGHT)
400: lc->sx += change;
401: else
402: lc->sy += change;
1.4 nicm 403:
1.3 nicm 404: /* If this is a leaf cell, that is all that is necessary. */
405: if (type == LAYOUT_WINDOWPANE)
406: return;
407:
408: /* Child cell runs in a different direction. */
409: if (lc->type != type) {
410: TAILQ_FOREACH(lcchild, &lc->cells, entry)
1.28 nicm 411: layout_resize_adjust(w, lcchild, type, change);
1.3 nicm 412: return;
1.1 nicm 413: }
414:
1.4 nicm 415: /*
416: * Child cell runs in the same direction. Adjust each child equally
1.3 nicm 417: * until no further change is possible.
418: */
419: while (change != 0) {
420: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
421: if (change == 0)
422: break;
423: if (change > 0) {
1.28 nicm 424: layout_resize_adjust(w, lcchild, type, 1);
1.3 nicm 425: change--;
426: continue;
427: }
1.28 nicm 428: if (layout_resize_check(w, lcchild, type) > 0) {
429: layout_resize_adjust(w, lcchild, type, -1);
1.3 nicm 430: change++;
431: }
432: }
1.1 nicm 433: }
434: }
435:
1.6 nicm 436: /* Destroy a cell and redistribute the space. */
437: void
1.28 nicm 438: layout_destroy_cell(struct window *w, struct layout_cell *lc,
439: struct layout_cell **lcroot)
1.6 nicm 440: {
441: struct layout_cell *lcother, *lcparent;
442:
443: /*
444: * If no parent, this is the last pane so window close is imminent and
445: * there is no need to resize anything.
446: */
447: lcparent = lc->parent;
448: if (lcparent == NULL) {
449: layout_free_cell(lc);
450: *lcroot = NULL;
451: return;
452: }
453:
454: /* Merge the space into the previous or next cell. */
455: if (lc == TAILQ_FIRST(&lcparent->cells))
456: lcother = TAILQ_NEXT(lc, entry);
457: else
458: lcother = TAILQ_PREV(lc, layout_cells, entry);
459: if (lcparent->type == LAYOUT_LEFTRIGHT)
1.28 nicm 460: layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1);
1.6 nicm 461: else
1.28 nicm 462: layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1);
1.6 nicm 463:
464: /* Remove this from the parent's list. */
465: TAILQ_REMOVE(&lcparent->cells, lc, entry);
466: layout_free_cell(lc);
467:
468: /*
469: * If the parent now has one cell, remove the parent from the tree and
470: * replace it by that cell.
471: */
472: lc = TAILQ_FIRST(&lcparent->cells);
473: if (TAILQ_NEXT(lc, entry) == NULL) {
474: TAILQ_REMOVE(&lcparent->cells, lc, entry);
475:
476: lc->parent = lcparent->parent;
477: if (lc->parent == NULL) {
478: lc->xoff = 0; lc->yoff = 0;
479: *lcroot = lc;
480: } else
481: TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
482:
483: layout_free_cell(lcparent);
484: }
485: }
486:
1.1 nicm 487: void
1.18 nicm 488: layout_init(struct window *w, struct window_pane *wp)
1.1 nicm 489: {
1.3 nicm 490: struct layout_cell *lc;
1.1 nicm 491:
1.3 nicm 492: lc = w->layout_root = layout_create_cell(NULL);
493: layout_set_size(lc, w->sx, w->sy, 0, 0);
1.18 nicm 494: layout_make_leaf(lc, wp);
1.1 nicm 495:
1.3 nicm 496: layout_fix_panes(w, w->sx, w->sy);
497: }
1.2 nicm 498:
1.3 nicm 499: void
500: layout_free(struct window *w)
501: {
502: layout_free_cell(w->layout_root);
503: }
1.1 nicm 504:
1.3 nicm 505: /* Resize the entire layout after window resize. */
506: void
507: layout_resize(struct window *w, u_int sx, u_int sy)
508: {
509: struct layout_cell *lc = w->layout_root;
510: int xlimit, ylimit, xchange, ychange;
1.1 nicm 511:
1.4 nicm 512: /*
1.3 nicm 513: * Adjust horizontally. Do not attempt to reduce the layout lower than
514: * the minimum (more than the amount returned by layout_resize_check).
1.4 nicm 515: *
1.3 nicm 516: * This can mean that the window size is smaller than the total layout
517: * size: redrawing this is handled at a higher level, but it does leave
518: * a problem with growing the window size here: if the current size is
519: * < the minimum, growing proportionately by adding to each pane is
520: * wrong as it would keep the layout size larger than the window size.
521: * Instead, spread the difference between the minimum and the new size
522: * out proportionately - this should leave the layout fitting the new
523: * window size.
524: */
525: xchange = sx - w->sx;
1.28 nicm 526: xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
1.3 nicm 527: if (xchange < 0 && xchange < -xlimit)
528: xchange = -xlimit;
529: if (xlimit == 0) {
530: if (sx <= lc->sx) /* lc->sx is minimum possible */
531: xchange = 0;
1.1 nicm 532: else
1.3 nicm 533: xchange = sx - lc->sx;
1.1 nicm 534: }
1.3 nicm 535: if (xchange != 0)
1.28 nicm 536: layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, xchange);
1.1 nicm 537:
1.3 nicm 538: /* Adjust vertically in a similar fashion. */
539: ychange = sy - w->sy;
1.28 nicm 540: ylimit = layout_resize_check(w, lc, LAYOUT_TOPBOTTOM);
1.3 nicm 541: if (ychange < 0 && ychange < -ylimit)
542: ychange = -ylimit;
543: if (ylimit == 0) {
544: if (sy <= lc->sy) /* lc->sy is minimum possible */
545: ychange = 0;
546: else
547: ychange = sy - lc->sy;
1.1 nicm 548: }
1.3 nicm 549: if (ychange != 0)
1.28 nicm 550: layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange);
1.4 nicm 551:
1.3 nicm 552: /* Fix cell offsets. */
553: layout_fix_offsets(lc);
554: layout_fix_panes(w, sx, sy);
1.1 nicm 555: }
556:
1.17 nicm 557: /* Resize a pane to an absolute size. */
558: void
559: layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
560: u_int new_size)
561: {
562: struct layout_cell *lc, *lcparent;
563: int change, size;
564:
565: lc = wp->layout_cell;
566:
567: /* Find next parent of the same type. */
568: lcparent = lc->parent;
569: while (lcparent != NULL && lcparent->type != type) {
570: lc = lcparent;
571: lcparent = lc->parent;
572: }
573: if (lcparent == NULL)
574: return;
575:
576: /* Work out the size adjustment. */
577: if (type == LAYOUT_LEFTRIGHT)
578: size = lc->sx;
579: else
580: size = lc->sy;
581: if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
582: change = size - new_size;
583: else
584: change = new_size - size;
585:
586: /* Resize the pane. */
1.30 nicm 587: layout_resize_pane(wp, type, change, 1);
1.17 nicm 588: }
589:
1.36 nicm 590: void
591: layout_resize_layout(struct window *w, struct layout_cell *lc,
592: enum layout_type type, int change, int opposite)
593: {
594: int needed, size;
595:
596: /* Grow or shrink the cell. */
597: needed = change;
598: while (needed != 0) {
599: if (change > 0) {
600: size = layout_resize_pane_grow(w, lc, type, needed,
601: opposite);
602: needed -= size;
603: } else {
604: size = layout_resize_pane_shrink(w, lc, type, needed);
605: needed += size;
606: }
607:
608: if (size == 0) /* no more change possible */
609: break;
610: }
611:
612: /* Fix cell offsets. */
613: layout_fix_offsets(w->layout_root);
614: layout_fix_panes(w, w->sx, w->sy);
615: notify_window("window-layout-changed", w);
616: }
617:
1.3 nicm 618: /* Resize a single pane within the layout. */
1.1 nicm 619: void
1.30 nicm 620: layout_resize_pane(struct window_pane *wp, enum layout_type type, int change,
621: int opposite)
1.1 nicm 622: {
1.28 nicm 623: struct layout_cell *lc, *lcparent;
1.1 nicm 624:
1.3 nicm 625: lc = wp->layout_cell;
1.1 nicm 626:
1.3 nicm 627: /* Find next parent of the same type. */
628: lcparent = lc->parent;
629: while (lcparent != NULL && lcparent->type != type) {
630: lc = lcparent;
631: lcparent = lc->parent;
632: }
633: if (lcparent == NULL)
634: return;
635:
636: /* If this is the last cell, move back one. */
637: if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
638: lc = TAILQ_PREV(lc, layout_cells, entry);
639:
1.36 nicm 640: layout_resize_layout(wp->window, lc, type, change, opposite);
1.3 nicm 641: }
1.1 nicm 642:
1.17 nicm 643: /* Helper function to grow pane. */
1.27 nicm 644: static int
1.28 nicm 645: layout_resize_pane_grow(struct window *w, struct layout_cell *lc,
1.30 nicm 646: enum layout_type type, int needed, int opposite)
1.3 nicm 647: {
648: struct layout_cell *lcadd, *lcremove;
1.30 nicm 649: u_int size = 0;
1.3 nicm 650:
651: /* Growing. Always add to the current cell. */
652: lcadd = lc;
1.4 nicm 653:
1.3 nicm 654: /* Look towards the tail for a suitable cell for reduction. */
655: lcremove = TAILQ_NEXT(lc, entry);
656: while (lcremove != NULL) {
1.28 nicm 657: size = layout_resize_check(w, lcremove, type);
1.3 nicm 658: if (size > 0)
659: break;
1.4 nicm 660: lcremove = TAILQ_NEXT(lcremove, entry);
1.1 nicm 661: }
662:
1.3 nicm 663: /* If none found, look towards the head. */
1.30 nicm 664: if (opposite && lcremove == NULL) {
1.3 nicm 665: lcremove = TAILQ_PREV(lc, layout_cells, entry);
666: while (lcremove != NULL) {
1.28 nicm 667: size = layout_resize_check(w, lcremove, type);
1.3 nicm 668: if (size > 0)
669: break;
670: lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
1.1 nicm 671: }
1.3 nicm 672: }
1.30 nicm 673: if (lcremove == NULL)
674: return (0);
1.1 nicm 675:
1.3 nicm 676: /* Change the cells. */
677: if (size > (u_int) needed)
678: size = needed;
1.28 nicm 679: layout_resize_adjust(w, lcadd, type, size);
680: layout_resize_adjust(w, lcremove, type, -size);
1.3 nicm 681: return (size);
682: }
1.1 nicm 683:
1.17 nicm 684: /* Helper function to shrink pane. */
1.27 nicm 685: static int
1.28 nicm 686: layout_resize_pane_shrink(struct window *w, struct layout_cell *lc,
687: enum layout_type type, int needed)
1.3 nicm 688: {
689: struct layout_cell *lcadd, *lcremove;
690: u_int size;
1.1 nicm 691:
1.3 nicm 692: /* Shrinking. Find cell to remove from by walking towards head. */
693: lcremove = lc;
694: do {
1.28 nicm 695: size = layout_resize_check(w, lcremove, type);
1.3 nicm 696: if (size != 0)
1.1 nicm 697: break;
1.3 nicm 698: lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
699: } while (lcremove != NULL);
700: if (lcremove == NULL)
701: return (0);
702:
703: /* And add onto the next cell (from the original cell). */
704: lcadd = TAILQ_NEXT(lc, entry);
705: if (lcadd == NULL)
706: return (0);
707:
708: /* Change the cells. */
709: if (size > (u_int) -needed)
710: size = -needed;
1.28 nicm 711: layout_resize_adjust(w, lcadd, type, size);
712: layout_resize_adjust(w, lcremove, type, -size);
1.3 nicm 713: return (size);
1.1 nicm 714: }
715:
1.5 nicm 716: /* Assign window pane to newly split cell. */
717: void
718: layout_assign_pane(struct layout_cell *lc, struct window_pane *wp)
719: {
720: layout_make_leaf(lc, wp);
721: layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
722: }
723:
1.29 nicm 724: /* Calculate the new pane size for resized parent. */
725: static u_int
726: layout_new_pane_size(struct window *w, u_int previous, struct layout_cell *lc,
727: enum layout_type type, u_int size, u_int count_left, u_int size_left)
728: {
729: u_int new_size, min, max, available;
730:
731: /* If this is the last cell, it can take all of the remaining size. */
732: if (count_left == 1)
733: return (size_left);
734:
735: /* How much is available in this parent? */
736: available = layout_resize_check(w, lc, type);
737:
738: /*
739: * Work out the minimum size of this cell and the new size
740: * proportionate to the previous size.
741: */
742: min = (PANE_MINIMUM + 1) * (count_left - 1);
743: if (type == LAYOUT_LEFTRIGHT) {
744: if (lc->sx - available > min)
745: min = lc->sx - available;
746: new_size = (lc->sx * size) / previous;
747: } else {
748: if (lc->sy - available > min)
749: min = lc->sy - available;
750: new_size = (lc->sy * size) / previous;
751: }
752:
753: /* Check against the maximum and minimum size. */
754: max = size_left - min;
755: if (new_size > max)
756: new_size = max;
757: if (new_size < PANE_MINIMUM)
758: new_size = PANE_MINIMUM;
759: return (new_size);
760: }
761:
762: /* Check if the cell and all its children can be resized to a specific size. */
763: static int
764: layout_set_size_check(struct window *w, struct layout_cell *lc,
765: enum layout_type type, int size)
766: {
767: struct layout_cell *lcchild;
768: u_int new_size, available, previous, count, idx;
769:
770: /* Cells with no children must just be bigger than minimum. */
771: if (lc->type == LAYOUT_WINDOWPANE)
772: return (size >= PANE_MINIMUM);
773: available = size;
774:
775: /* Count number of children. */
776: count = 0;
777: TAILQ_FOREACH(lcchild, &lc->cells, entry)
778: count++;
779:
780: /* Check new size will work for each child. */
781: if (lc->type == type) {
782: if (type == LAYOUT_LEFTRIGHT)
783: previous = lc->sx;
784: else
785: previous = lc->sy;
786:
787: idx = 0;
788: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
789: new_size = layout_new_pane_size(w, previous, lcchild,
790: type, size, count - idx, available);
791: if (new_size > available)
792: return (0);
793:
794: available -= (new_size + 1);
795: if (!layout_set_size_check(w, lcchild, type, new_size))
796: return (0);
797:
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.29 nicm 870: int insert_before, int full_size)
1.1 nicm 871: {
1.9 nicm 872: struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
1.3 nicm 873: u_int sx, sy, xoff, yoff, size1, size2;
1.29 nicm 874: u_int new_size, saved_size, resize_first = 0;
1.1 nicm 875:
1.29 nicm 876: /*
877: * If full_size is specified, add a new cell at the top of the window
878: * layout. Otherwise, split the cell for the current pane.
879: */
880: if (full_size)
881: lc = wp->window->layout_root;
882: else
883: lc = wp->layout_cell;
1.1 nicm 884:
1.3 nicm 885: /* Copy the old cell size. */
886: sx = lc->sx;
887: sy = lc->sy;
888: xoff = lc->xoff;
889: yoff = lc->yoff;
890:
891: /* Check there is enough space for the two new panes. */
892: switch (type) {
893: case LAYOUT_LEFTRIGHT:
894: if (sx < PANE_MINIMUM * 2 + 1)
1.5 nicm 895: return (NULL);
1.3 nicm 896: break;
897: case LAYOUT_TOPBOTTOM:
898: if (sy < PANE_MINIMUM * 2 + 1)
1.5 nicm 899: return (NULL);
1.3 nicm 900: break;
901: default:
902: fatalx("bad layout type");
903: }
1.4 nicm 904:
1.29 nicm 905: /*
906: * Calculate new cell sizes. size is the target size or -1 for middle
907: * split, size1 is the size of the top/left and size2 the bottom/right.
908: */
909: if (type == LAYOUT_LEFTRIGHT)
910: saved_size = sx;
911: else
912: saved_size = sy;
913: if (size < 0)
914: size2 = ((saved_size + 1) / 2) - 1;
915: else if (insert_before)
916: size2 = saved_size - size - 1;
917: else
918: size2 = size;
919: if (size2 < PANE_MINIMUM)
920: size2 = PANE_MINIMUM;
921: else if (size2 > saved_size - 2)
922: size2 = saved_size - 2;
923: size1 = saved_size - 1 - size2;
924:
925: /* Which size are we using? */
926: if (insert_before)
927: new_size = size2;
928: else
929: new_size = size1;
930:
931: /* Confirm there is enough space for full size pane. */
932: if (full_size && !layout_set_size_check(wp->window, lc, type, new_size))
933: return (NULL);
934:
1.3 nicm 935: if (lc->parent != NULL && lc->parent->type == type) {
936: /*
937: * If the parent exists and is of the same type as the split,
938: * create a new cell and insert it after this one.
939: */
1.9 nicm 940: lcparent = lc->parent;
941: lcnew = layout_create_cell(lcparent);
942: if (insert_before)
943: TAILQ_INSERT_BEFORE(lc, lcnew, entry);
944: else
945: TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
1.29 nicm 946: } else if (full_size && lc->parent == NULL && lc->type == type) {
947: /*
948: * If the new full size pane is the same type as the root
949: * split, insert the new pane under the existing root cell
950: * instead of creating a new root cell. The existing layout
951: * must be resized before inserting the new cell.
952: */
953: if (lc->type == LAYOUT_LEFTRIGHT) {
954: lc->sx = new_size;
955: layout_resize_child_cells(wp->window, lc);
956: lc->sx = saved_size;
957: } else if (lc->type == LAYOUT_TOPBOTTOM) {
958: lc->sy = new_size;
959: layout_resize_child_cells(wp->window, lc);
960: lc->sy = saved_size;
961: }
962: resize_first = 1;
963:
964: /* Create the new cell. */
965: lcnew = layout_create_cell(lc);
1.32 nicm 966: size = saved_size - 1 - new_size;
1.29 nicm 967: if (lc->type == LAYOUT_LEFTRIGHT)
1.32 nicm 968: layout_set_size(lcnew, size, sy, 0, 0);
1.29 nicm 969: else if (lc->type == LAYOUT_TOPBOTTOM)
1.32 nicm 970: layout_set_size(lcnew, sx, size, 0, 0);
1.29 nicm 971: if (insert_before)
972: TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry);
973: else
974: TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
1.3 nicm 975: } else {
976: /*
977: * Otherwise create a new parent and insert it.
978: */
1.4 nicm 979:
1.3 nicm 980: /* Create and insert the replacement parent. */
981: lcparent = layout_create_cell(lc->parent);
982: layout_make_node(lcparent, type);
983: layout_set_size(lcparent, sx, sy, xoff, yoff);
984: if (lc->parent == NULL)
985: wp->window->layout_root = lcparent;
986: else
987: TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
1.4 nicm 988:
1.3 nicm 989: /* Insert the old cell. */
990: lc->parent = lcparent;
991: TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
1.4 nicm 992:
1.3 nicm 993: /* Create the new child cell. */
994: lcnew = layout_create_cell(lcparent);
1.9 nicm 995: if (insert_before)
996: TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry);
997: else
998: TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
999: }
1000: if (insert_before) {
1001: lc1 = lcnew;
1002: lc2 = lc;
1003: } else {
1004: lc1 = lc;
1005: lc2 = lcnew;
1.3 nicm 1006: }
1007:
1.29 nicm 1008: /*
1009: * Set new cell sizes. size1 is the size of the top/left and size2 the
1010: * bottom/right.
1.3 nicm 1011: */
1.29 nicm 1012: if (!resize_first && type == LAYOUT_LEFTRIGHT) {
1.9 nicm 1013: layout_set_size(lc1, size1, sy, xoff, yoff);
1014: layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff);
1.29 nicm 1015: } else if (!resize_first && type == LAYOUT_TOPBOTTOM) {
1.9 nicm 1016: layout_set_size(lc1, sx, size1, xoff, yoff);
1017: layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1);
1.3 nicm 1018: }
1.29 nicm 1019: if (full_size) {
1020: if (!resize_first)
1021: layout_resize_child_cells(wp->window, lc);
1022: layout_fix_offsets(wp->window->layout_root);
1023: } else
1024: layout_make_leaf(lc, wp);
1.1 nicm 1025:
1.5 nicm 1026: return (lcnew);
1.3 nicm 1027: }
1.1 nicm 1028:
1.6 nicm 1029: /* Destroy the cell associated with a pane. */
1.3 nicm 1030: void
1031: layout_close_pane(struct window_pane *wp)
1032: {
1.28 nicm 1033: struct window *w = wp->window;
1034:
1.6 nicm 1035: /* Remove the cell. */
1.28 nicm 1036: layout_destroy_cell(w, wp->layout_cell, &w->layout_root);
1.1 nicm 1037:
1.6 nicm 1038: /* Fix pane offsets and sizes. */
1.28 nicm 1039: if (w->layout_root != NULL) {
1040: layout_fix_offsets(w->layout_root);
1041: layout_fix_panes(w, w->sx, w->sy);
1.3 nicm 1042: }
1.31 nicm 1043: notify_window("window-layout-changed", w);
1.33 nicm 1044: }
1045:
1046: int
1047: layout_spread_cell(struct window *w, struct layout_cell *parent)
1048: {
1049: struct layout_cell *lc;
1050: u_int number, each, size;
1051: int change, changed;
1052:
1053: number = 0;
1054: TAILQ_FOREACH (lc, &parent->cells, entry)
1055: number++;
1056: if (number <= 1)
1057: return (0);
1058:
1059: if (parent->type == LAYOUT_LEFTRIGHT)
1060: size = parent->sx;
1061: else if (parent->type == LAYOUT_TOPBOTTOM)
1062: size = parent->sy;
1063: else
1064: return (0);
1065: each = (size - (number - 1)) / number;
1066:
1067: changed = 0;
1068: TAILQ_FOREACH (lc, &parent->cells, entry) {
1069: if (TAILQ_NEXT(lc, entry) == NULL)
1.35 nicm 1070: each = size - ((each + 1) * (number - 1));
1.33 nicm 1071: change = 0;
1072: if (parent->type == LAYOUT_LEFTRIGHT) {
1073: change = each - (int)lc->sx;
1074: layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
1075: } else if (parent->type == LAYOUT_TOPBOTTOM) {
1076: change = each - (int)lc->sy;
1077: layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
1078: }
1079: if (change != 0)
1080: changed = 1;
1081: }
1082: return (changed);
1083: }
1084:
1085: void
1086: layout_spread_out(struct window_pane *wp)
1087: {
1088: struct layout_cell *parent;
1089: struct window *w = wp->window;
1090:
1091: parent = wp->layout_cell->parent;
1092: if (parent == NULL)
1093: return;
1094:
1095: do {
1096: if (layout_spread_cell(w, parent)) {
1097: layout_fix_offsets(parent);
1098: layout_fix_panes(w, w->sx, w->sy);
1099: break;
1100: }
1101: } while ((parent = parent->parent) != NULL);
1.1 nicm 1102: }