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