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