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