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