1*5df986e2Snicm /* $OpenBSD: layout.c,v 1.52 2024/12/17 08:40:24 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 498ca8272Snicm * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> 5824e896bSnicm * Copyright (c) 2016 Stephen Kent <smkent@smkent.net> 6311827fbSnicm * 7311827fbSnicm * Permission to use, copy, modify, and distribute this software for any 8311827fbSnicm * purpose with or without fee is hereby granted, provided that the above 9311827fbSnicm * copyright notice and this permission notice appear in all copies. 10311827fbSnicm * 11311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 16311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 17311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18311827fbSnicm */ 19311827fbSnicm 20311827fbSnicm #include <sys/types.h> 21311827fbSnicm 22af9e4c5dSnicm #include <stdlib.h> 23311827fbSnicm 24311827fbSnicm #include "tmux.h" 25311827fbSnicm 26311827fbSnicm /* 27af9e4c5dSnicm * The window layout is a tree of cells each of which can be one of: a 28af9e4c5dSnicm * left-right container for a list of cells, a top-bottom container for a list 29af9e4c5dSnicm * of cells, or a container for a window pane. 30311827fbSnicm * 31af9e4c5dSnicm * Each window has a pointer to the root of its layout tree (containing its 32af9e4c5dSnicm * panes), every pane has a pointer back to the cell containing it, and each 33af9e4c5dSnicm * cell a pointer to its parent cell. 34311827fbSnicm */ 35311827fbSnicm 3607b91187Snicm static u_int layout_resize_check(struct window *, struct layout_cell *, 3707b91187Snicm enum layout_type); 3807b91187Snicm static int layout_resize_pane_grow(struct window *, struct layout_cell *, 39f4d5873aSnicm enum layout_type, int, int); 4007b91187Snicm static int layout_resize_pane_shrink(struct window *, struct layout_cell *, 41bc3b19faSnicm enum layout_type, int); 42824e896bSnicm static u_int layout_new_pane_size(struct window *, u_int, 43824e896bSnicm struct layout_cell *, enum layout_type, u_int, u_int, 44824e896bSnicm u_int); 45824e896bSnicm static int layout_set_size_check(struct window *, struct layout_cell *, 46824e896bSnicm enum layout_type, int); 47824e896bSnicm static void layout_resize_child_cells(struct window *, 48824e896bSnicm struct layout_cell *); 49311827fbSnicm 50af9e4c5dSnicm struct layout_cell * 51af9e4c5dSnicm layout_create_cell(struct layout_cell *lcparent) 52311827fbSnicm { 53af9e4c5dSnicm struct layout_cell *lc; 54311827fbSnicm 55af9e4c5dSnicm lc = xmalloc(sizeof *lc); 56af9e4c5dSnicm lc->type = LAYOUT_WINDOWPANE; 57af9e4c5dSnicm lc->parent = lcparent; 58311827fbSnicm 59af9e4c5dSnicm TAILQ_INIT(&lc->cells); 60311827fbSnicm 61af9e4c5dSnicm lc->sx = UINT_MAX; 62af9e4c5dSnicm lc->sy = UINT_MAX; 63311827fbSnicm 64af9e4c5dSnicm lc->xoff = UINT_MAX; 65af9e4c5dSnicm lc->yoff = UINT_MAX; 66311827fbSnicm 67af9e4c5dSnicm lc->wp = NULL; 68af9e4c5dSnicm 69af9e4c5dSnicm return (lc); 70311827fbSnicm } 71311827fbSnicm 72311827fbSnicm void 73af9e4c5dSnicm layout_free_cell(struct layout_cell *lc) 74311827fbSnicm { 75af9e4c5dSnicm struct layout_cell *lcchild; 76af9e4c5dSnicm 77af9e4c5dSnicm switch (lc->type) { 78af9e4c5dSnicm case LAYOUT_LEFTRIGHT: 79af9e4c5dSnicm case LAYOUT_TOPBOTTOM: 80af9e4c5dSnicm while (!TAILQ_EMPTY(&lc->cells)) { 81af9e4c5dSnicm lcchild = TAILQ_FIRST(&lc->cells); 82af9e4c5dSnicm TAILQ_REMOVE(&lc->cells, lcchild, entry); 83af9e4c5dSnicm layout_free_cell(lcchild); 84af9e4c5dSnicm } 85af9e4c5dSnicm break; 86af9e4c5dSnicm case LAYOUT_WINDOWPANE: 87af9e4c5dSnicm if (lc->wp != NULL) 88af9e4c5dSnicm lc->wp->layout_cell = NULL; 89af9e4c5dSnicm break; 90af9e4c5dSnicm } 91af9e4c5dSnicm 927d053cf9Snicm free(lc); 93311827fbSnicm } 94311827fbSnicm 95311827fbSnicm void 96af9e4c5dSnicm layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) 97311827fbSnicm { 98af9e4c5dSnicm struct layout_cell *lcchild; 9968eb3405Snicm const char *type; 100af9e4c5dSnicm 10168eb3405Snicm switch (lc->type) { 10268eb3405Snicm case LAYOUT_LEFTRIGHT: 10368eb3405Snicm type = "LEFTRIGHT"; 10468eb3405Snicm break; 10568eb3405Snicm case LAYOUT_TOPBOTTOM: 10668eb3405Snicm type = "TOPBOTTOM"; 10768eb3405Snicm break; 10868eb3405Snicm case LAYOUT_WINDOWPANE: 10968eb3405Snicm type = "WINDOWPANE"; 11068eb3405Snicm break; 11168eb3405Snicm default: 11268eb3405Snicm type = "UNKNOWN"; 11368eb3405Snicm break; 11468eb3405Snicm } 11568eb3405Snicm log_debug("%s:%*s%p type %s [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, 11668eb3405Snicm " ", lc, type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, 117d5221700Snicm lc->sy); 118af9e4c5dSnicm switch (lc->type) { 119af9e4c5dSnicm case LAYOUT_LEFTRIGHT: 120af9e4c5dSnicm case LAYOUT_TOPBOTTOM: 121af9e4c5dSnicm TAILQ_FOREACH(lcchild, &lc->cells, entry) 122af9e4c5dSnicm layout_print_cell(lcchild, hdr, n + 1); 123af9e4c5dSnicm break; 124af9e4c5dSnicm case LAYOUT_WINDOWPANE: 125af9e4c5dSnicm break; 126af9e4c5dSnicm } 127311827fbSnicm } 128311827fbSnicm 1293d1607c5Snicm struct layout_cell * 1303d1607c5Snicm layout_search_by_border(struct layout_cell *lc, u_int x, u_int y) 1313d1607c5Snicm { 1323d1607c5Snicm struct layout_cell *lcchild, *last = NULL; 1333d1607c5Snicm 1343d1607c5Snicm TAILQ_FOREACH(lcchild, &lc->cells, entry) { 1353d1607c5Snicm if (x >= lcchild->xoff && x < lcchild->xoff + lcchild->sx && 1363d1607c5Snicm y >= lcchild->yoff && y < lcchild->yoff + lcchild->sy) { 1373d1607c5Snicm /* Inside the cell - recurse. */ 1383d1607c5Snicm return (layout_search_by_border(lcchild, x, y)); 1393d1607c5Snicm } 1403d1607c5Snicm 1413d1607c5Snicm if (last == NULL) { 1423d1607c5Snicm last = lcchild; 1433d1607c5Snicm continue; 1443d1607c5Snicm } 1453d1607c5Snicm 1463d1607c5Snicm switch (lc->type) { 1473d1607c5Snicm case LAYOUT_LEFTRIGHT: 1483d1607c5Snicm if (x < lcchild->xoff && x >= last->xoff + last->sx) 1493d1607c5Snicm return (last); 1503d1607c5Snicm break; 1513d1607c5Snicm case LAYOUT_TOPBOTTOM: 1523d1607c5Snicm if (y < lcchild->yoff && y >= last->yoff + last->sy) 1533d1607c5Snicm return (last); 1543d1607c5Snicm break; 1553d1607c5Snicm case LAYOUT_WINDOWPANE: 1563d1607c5Snicm break; 1573d1607c5Snicm } 1583d1607c5Snicm 1593d1607c5Snicm last = lcchild; 1603d1607c5Snicm } 1613d1607c5Snicm 1623d1607c5Snicm return (NULL); 1633d1607c5Snicm } 1643d1607c5Snicm 165311827fbSnicm void 166d5221700Snicm layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, 167d5221700Snicm u_int yoff) 168311827fbSnicm { 169af9e4c5dSnicm lc->sx = sx; 170af9e4c5dSnicm lc->sy = sy; 171311827fbSnicm 172af9e4c5dSnicm lc->xoff = xoff; 173af9e4c5dSnicm lc->yoff = yoff; 174311827fbSnicm } 175311827fbSnicm 176311827fbSnicm void 177af9e4c5dSnicm layout_make_leaf(struct layout_cell *lc, struct window_pane *wp) 178af9e4c5dSnicm { 179af9e4c5dSnicm lc->type = LAYOUT_WINDOWPANE; 180af9e4c5dSnicm 181af9e4c5dSnicm TAILQ_INIT(&lc->cells); 182af9e4c5dSnicm 183af9e4c5dSnicm wp->layout_cell = lc; 184af9e4c5dSnicm lc->wp = wp; 185af9e4c5dSnicm } 186af9e4c5dSnicm 187af9e4c5dSnicm void 188af9e4c5dSnicm layout_make_node(struct layout_cell *lc, enum layout_type type) 189af9e4c5dSnicm { 190af9e4c5dSnicm if (type == LAYOUT_WINDOWPANE) 191af9e4c5dSnicm fatalx("bad layout type"); 192af9e4c5dSnicm lc->type = type; 193af9e4c5dSnicm 194af9e4c5dSnicm TAILQ_INIT(&lc->cells); 195af9e4c5dSnicm 196af9e4c5dSnicm if (lc->wp != NULL) 197af9e4c5dSnicm lc->wp->layout_cell = NULL; 198af9e4c5dSnicm lc->wp = NULL; 199af9e4c5dSnicm } 200af9e4c5dSnicm 2019bc2f29eSnicm /* Fix cell offsets for a child cell. */ 20242fbd26aSnicm static void 20342fbd26aSnicm layout_fix_offsets1(struct layout_cell *lc) 204af9e4c5dSnicm { 205af9e4c5dSnicm struct layout_cell *lcchild; 206af9e4c5dSnicm u_int xoff, yoff; 207af9e4c5dSnicm 208af9e4c5dSnicm if (lc->type == LAYOUT_LEFTRIGHT) { 209af9e4c5dSnicm xoff = lc->xoff; 210af9e4c5dSnicm TAILQ_FOREACH(lcchild, &lc->cells, entry) { 211af9e4c5dSnicm lcchild->xoff = xoff; 212af9e4c5dSnicm lcchild->yoff = lc->yoff; 213af9e4c5dSnicm if (lcchild->type != LAYOUT_WINDOWPANE) 21442fbd26aSnicm layout_fix_offsets1(lcchild); 215af9e4c5dSnicm xoff += lcchild->sx + 1; 216af9e4c5dSnicm } 217af9e4c5dSnicm } else { 218af9e4c5dSnicm yoff = lc->yoff; 219af9e4c5dSnicm TAILQ_FOREACH(lcchild, &lc->cells, entry) { 220af9e4c5dSnicm lcchild->xoff = lc->xoff; 221af9e4c5dSnicm lcchild->yoff = yoff; 222af9e4c5dSnicm if (lcchild->type != LAYOUT_WINDOWPANE) 22342fbd26aSnicm layout_fix_offsets1(lcchild); 224af9e4c5dSnicm yoff += lcchild->sy + 1; 225af9e4c5dSnicm } 226af9e4c5dSnicm } 227af9e4c5dSnicm } 228af9e4c5dSnicm 22942fbd26aSnicm /* Update cell offsets based on their sizes. */ 23042fbd26aSnicm void 23142fbd26aSnicm layout_fix_offsets(struct window *w) 23242fbd26aSnicm { 23342fbd26aSnicm struct layout_cell *lc = w->layout_root; 23442fbd26aSnicm 23542fbd26aSnicm lc->xoff = 0; 23642fbd26aSnicm lc->yoff = 0; 23742fbd26aSnicm 23842fbd26aSnicm layout_fix_offsets1(lc); 23942fbd26aSnicm } 24042fbd26aSnicm 2419bc2f29eSnicm /* Is this a top cell? */ 242bc3b19faSnicm static int 2439bc2f29eSnicm layout_cell_is_top(struct window *w, struct layout_cell *lc) 244bc3b19faSnicm { 245c1cd4700Snicm struct layout_cell *next; 246bc3b19faSnicm 2479bc2f29eSnicm while (lc != w->layout_root) { 2489bc2f29eSnicm next = lc->parent; 2499bc2f29eSnicm if (next->type == LAYOUT_TOPBOTTOM && 2509bc2f29eSnicm lc != TAILQ_FIRST(&next->cells)) 251bc3b19faSnicm return (0); 2529bc2f29eSnicm lc = next; 253bc3b19faSnicm } 254bc3b19faSnicm return (1); 255bc3b19faSnicm } 256bc3b19faSnicm 2579bc2f29eSnicm /* Is this a bottom cell? */ 2589bc2f29eSnicm static int 2599bc2f29eSnicm layout_cell_is_bottom(struct window *w, struct layout_cell *lc) 2609bc2f29eSnicm { 2619bc2f29eSnicm struct layout_cell *next; 2629bc2f29eSnicm 2639bc2f29eSnicm while (lc != w->layout_root) { 2649bc2f29eSnicm next = lc->parent; 2659bc2f29eSnicm if (next->type == LAYOUT_TOPBOTTOM && 2669bc2f29eSnicm lc != TAILQ_LAST(&next->cells, layout_cells)) 2679bc2f29eSnicm return (0); 2689bc2f29eSnicm lc = next; 2699bc2f29eSnicm } 2709bc2f29eSnicm return (1); 2719bc2f29eSnicm } 2729bc2f29eSnicm 2739bc2f29eSnicm /* 2749bc2f29eSnicm * Returns 1 if we need to add an extra line for the pane status line. This is 2759bc2f29eSnicm * the case for the most upper or lower panes only. 2769bc2f29eSnicm */ 2779bc2f29eSnicm static int 278d2117533Snicm layout_add_horizontal_border(struct window *w, struct layout_cell *lc, 279d2117533Snicm int status) 2809bc2f29eSnicm { 2819bc2f29eSnicm if (status == PANE_STATUS_TOP) 2829bc2f29eSnicm return (layout_cell_is_top(w, lc)); 2839bc2f29eSnicm if (status == PANE_STATUS_BOTTOM) 2849bc2f29eSnicm return (layout_cell_is_bottom(w, lc)); 2859bc2f29eSnicm return (0); 2869bc2f29eSnicm } 2879bc2f29eSnicm 288af9e4c5dSnicm /* Update pane offsets and sizes based on their cells. */ 289af9e4c5dSnicm void 290baddd6b2Snicm layout_fix_panes(struct window *w, struct window_pane *skip) 291311827fbSnicm { 292311827fbSnicm struct window_pane *wp; 293af9e4c5dSnicm struct layout_cell *lc; 294dd0df669Snicm int status, scrollbars, sb_pos, sb_w, sb_pad; 2958b458060Snicm u_int sx, sy; 296311827fbSnicm 297bc3b19faSnicm status = options_get_number(w->options, "pane-border-status"); 298d2117533Snicm scrollbars = options_get_number(w->options, "pane-scrollbars"); 299d2117533Snicm sb_pos = options_get_number(w->options, "pane-scrollbars-position"); 300d2117533Snicm 301311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 302baddd6b2Snicm if ((lc = wp->layout_cell) == NULL || wp == skip) 303af9e4c5dSnicm continue; 304bc3b19faSnicm 305af9e4c5dSnicm wp->xoff = lc->xoff; 306af9e4c5dSnicm wp->yoff = lc->yoff; 307d2117533Snicm sx = lc->sx; 308d2117533Snicm sy = lc->sy; 309af9e4c5dSnicm 310d2117533Snicm if (layout_add_horizontal_border(w, lc, status)) { 3119bc2f29eSnicm if (status == PANE_STATUS_TOP) 3129bc2f29eSnicm wp->yoff++; 313d2117533Snicm sy--; 314d2117533Snicm } 315d2117533Snicm 3168b458060Snicm if (window_pane_show_scrollbar(wp, scrollbars)) { 317dd0df669Snicm sb_w = wp->scrollbar_style.width; 318dd0df669Snicm sb_pad = wp->scrollbar_style.pad; 319dd0df669Snicm if (sb_w < 1) 320dd0df669Snicm sb_w = 1; 321dd0df669Snicm if (sb_pad < 0) 322dd0df669Snicm sb_pad = 0; 323d2117533Snicm if (sb_pos == PANE_SCROLLBARS_LEFT) { 324dd0df669Snicm if ((int)sx - sb_w < PANE_MINIMUM) { 325dd0df669Snicm wp->xoff = wp->xoff + 326dd0df669Snicm (int)sx - PANE_MINIMUM; 327dd0df669Snicm sx = PANE_MINIMUM; 328dd0df669Snicm } else { 329dd0df669Snicm sx = sx - sb_w - sb_pad; 330dd0df669Snicm wp->xoff = wp->xoff + sb_w + sb_pad; 331dd0df669Snicm } 332d2117533Snicm } else /* sb_pos == PANE_SCROLLBARS_RIGHT */ 333dd0df669Snicm if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) 334dd0df669Snicm sx = PANE_MINIMUM; 335dd0df669Snicm else 336dd0df669Snicm sx = sx - sb_w - sb_pad; 337d2117533Snicm wp->flags |= PANE_REDRAWSCROLLBAR; 338d2117533Snicm } 339d2117533Snicm 340d2117533Snicm window_pane_resize(wp, sx, sy); 341311827fbSnicm } 342311827fbSnicm } 343311827fbSnicm 344f4611a41Snicm /* Count the number of available cells in a layout. */ 345f4611a41Snicm u_int 346f4611a41Snicm layout_count_cells(struct layout_cell *lc) 347f4611a41Snicm { 348f4611a41Snicm struct layout_cell *lcchild; 349824e896bSnicm u_int count; 350f4611a41Snicm 351f4611a41Snicm switch (lc->type) { 352f4611a41Snicm case LAYOUT_WINDOWPANE: 353f4611a41Snicm return (1); 354f4611a41Snicm case LAYOUT_LEFTRIGHT: 355f4611a41Snicm case LAYOUT_TOPBOTTOM: 356824e896bSnicm count = 0; 357f4611a41Snicm TAILQ_FOREACH(lcchild, &lc->cells, entry) 358824e896bSnicm count += layout_count_cells(lcchild); 359824e896bSnicm return (count); 360f4611a41Snicm default: 361f4611a41Snicm fatalx("bad layout type"); 362f4611a41Snicm } 363f4611a41Snicm } 364f4611a41Snicm 365af9e4c5dSnicm /* Calculate how much size is available to be removed from a cell. */ 36607b91187Snicm static u_int 36707b91187Snicm layout_resize_check(struct window *w, struct layout_cell *lc, 36807b91187Snicm enum layout_type type) 369311827fbSnicm { 370af9e4c5dSnicm struct layout_cell *lcchild; 371dd0df669Snicm struct style *sb_style = &w->active->scrollbar_style; 372af9e4c5dSnicm u_int available, minimum; 373d2117533Snicm int status, scrollbars; 374311827fbSnicm 375781ff0fdSnicm status = options_get_number(w->options, "pane-border-status"); 376d2117533Snicm scrollbars = options_get_number(w->options, "pane-scrollbars"); 377d2117533Snicm 378af9e4c5dSnicm if (lc->type == LAYOUT_WINDOWPANE) { 379af9e4c5dSnicm /* Space available in this cell only. */ 3809bc2f29eSnicm if (type == LAYOUT_LEFTRIGHT) { 381af9e4c5dSnicm available = lc->sx; 382d2117533Snicm if (scrollbars) 383dd0df669Snicm minimum = PANE_MINIMUM + sb_style->width + 384dd0df669Snicm sb_style->pad; 385d2117533Snicm else 3869bc2f29eSnicm minimum = PANE_MINIMUM; 3879bc2f29eSnicm } else { 388af9e4c5dSnicm available = lc->sy; 389d2117533Snicm if (layout_add_horizontal_border(w, lc, status)) 3909bc2f29eSnicm minimum = PANE_MINIMUM + 1; 3919bc2f29eSnicm else 3929bc2f29eSnicm minimum = PANE_MINIMUM; 39307b91187Snicm } 39407b91187Snicm if (available > minimum) 39507b91187Snicm available -= minimum; 396311827fbSnicm else 397af9e4c5dSnicm available = 0; 398af9e4c5dSnicm } else if (lc->type == type) { 399af9e4c5dSnicm /* Same type: total of available space in all child cells. */ 400af9e4c5dSnicm available = 0; 401af9e4c5dSnicm TAILQ_FOREACH(lcchild, &lc->cells, entry) 40207b91187Snicm available += layout_resize_check(w, lcchild, type); 403af9e4c5dSnicm } else { 404af9e4c5dSnicm /* Different type: minimum of available space in child cells. */ 405af9e4c5dSnicm minimum = UINT_MAX; 406af9e4c5dSnicm TAILQ_FOREACH(lcchild, &lc->cells, entry) { 40707b91187Snicm available = layout_resize_check(w, lcchild, type); 408af9e4c5dSnicm if (available < minimum) 409af9e4c5dSnicm minimum = available; 410af9e4c5dSnicm } 411af9e4c5dSnicm available = minimum; 412311827fbSnicm } 413311827fbSnicm 414af9e4c5dSnicm return (available); 415311827fbSnicm } 416311827fbSnicm 417af9e4c5dSnicm /* 418af9e4c5dSnicm * Adjust cell size evenly, including altering its children. This function 419af9e4c5dSnicm * expects the change to have already been bounded to the space available. 420af9e4c5dSnicm */ 421311827fbSnicm void 42207b91187Snicm layout_resize_adjust(struct window *w, struct layout_cell *lc, 42307b91187Snicm enum layout_type type, int change) 424311827fbSnicm { 425af9e4c5dSnicm struct layout_cell *lcchild; 426311827fbSnicm 427af9e4c5dSnicm /* Adjust the cell size. */ 428af9e4c5dSnicm if (type == LAYOUT_LEFTRIGHT) 429af9e4c5dSnicm lc->sx += change; 430af9e4c5dSnicm else 431af9e4c5dSnicm lc->sy += change; 432af9e4c5dSnicm 433af9e4c5dSnicm /* If this is a leaf cell, that is all that is necessary. */ 434af9e4c5dSnicm if (type == LAYOUT_WINDOWPANE) 435311827fbSnicm return; 436311827fbSnicm 437af9e4c5dSnicm /* Child cell runs in a different direction. */ 438af9e4c5dSnicm if (lc->type != type) { 439af9e4c5dSnicm TAILQ_FOREACH(lcchild, &lc->cells, entry) 44007b91187Snicm layout_resize_adjust(w, lcchild, type, change); 441311827fbSnicm return; 442311827fbSnicm } 443311827fbSnicm 444af9e4c5dSnicm /* 445af9e4c5dSnicm * Child cell runs in the same direction. Adjust each child equally 446af9e4c5dSnicm * until no further change is possible. 447af9e4c5dSnicm */ 448af9e4c5dSnicm while (change != 0) { 449af9e4c5dSnicm TAILQ_FOREACH(lcchild, &lc->cells, entry) { 450af9e4c5dSnicm if (change == 0) 451af9e4c5dSnicm break; 452af9e4c5dSnicm if (change > 0) { 45307b91187Snicm layout_resize_adjust(w, lcchild, type, 1); 454af9e4c5dSnicm change--; 455311827fbSnicm continue; 456311827fbSnicm } 45707b91187Snicm if (layout_resize_check(w, lcchild, type) > 0) { 45807b91187Snicm layout_resize_adjust(w, lcchild, type, -1); 459af9e4c5dSnicm change++; 460311827fbSnicm } 461af9e4c5dSnicm } 462311827fbSnicm } 463311827fbSnicm } 464311827fbSnicm 465f4611a41Snicm /* Destroy a cell and redistribute the space. */ 466f4611a41Snicm void 46707b91187Snicm layout_destroy_cell(struct window *w, struct layout_cell *lc, 46807b91187Snicm struct layout_cell **lcroot) 469f4611a41Snicm { 470f4611a41Snicm struct layout_cell *lcother, *lcparent; 471f4611a41Snicm 472f4611a41Snicm /* 473f4611a41Snicm * If no parent, this is the last pane so window close is imminent and 474f4611a41Snicm * there is no need to resize anything. 475f4611a41Snicm */ 476f4611a41Snicm lcparent = lc->parent; 477f4611a41Snicm if (lcparent == NULL) { 478f4611a41Snicm layout_free_cell(lc); 479f4611a41Snicm *lcroot = NULL; 480f4611a41Snicm return; 481f4611a41Snicm } 482f4611a41Snicm 483f4611a41Snicm /* Merge the space into the previous or next cell. */ 484f4611a41Snicm if (lc == TAILQ_FIRST(&lcparent->cells)) 485f4611a41Snicm lcother = TAILQ_NEXT(lc, entry); 486f4611a41Snicm else 487f4611a41Snicm lcother = TAILQ_PREV(lc, layout_cells, entry); 4889a0e05b8Snicm if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT) 48907b91187Snicm layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1); 4909a0e05b8Snicm else if (lcother != NULL) 49107b91187Snicm layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1); 492f4611a41Snicm 493f4611a41Snicm /* Remove this from the parent's list. */ 494f4611a41Snicm TAILQ_REMOVE(&lcparent->cells, lc, entry); 495f4611a41Snicm layout_free_cell(lc); 496f4611a41Snicm 497f4611a41Snicm /* 498f4611a41Snicm * If the parent now has one cell, remove the parent from the tree and 499f4611a41Snicm * replace it by that cell. 500f4611a41Snicm */ 501f4611a41Snicm lc = TAILQ_FIRST(&lcparent->cells); 502f4611a41Snicm if (TAILQ_NEXT(lc, entry) == NULL) { 503f4611a41Snicm TAILQ_REMOVE(&lcparent->cells, lc, entry); 504f4611a41Snicm 505f4611a41Snicm lc->parent = lcparent->parent; 506f4611a41Snicm if (lc->parent == NULL) { 507f4611a41Snicm lc->xoff = 0; lc->yoff = 0; 508f4611a41Snicm *lcroot = lc; 509f4611a41Snicm } else 510f4611a41Snicm TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry); 511f4611a41Snicm 512f4611a41Snicm layout_free_cell(lcparent); 513f4611a41Snicm } 514f4611a41Snicm } 515f4611a41Snicm 516311827fbSnicm void 517b4a3311eSnicm layout_init(struct window *w, struct window_pane *wp) 518311827fbSnicm { 519af9e4c5dSnicm struct layout_cell *lc; 520311827fbSnicm 521af9e4c5dSnicm lc = w->layout_root = layout_create_cell(NULL); 522af9e4c5dSnicm layout_set_size(lc, w->sx, w->sy, 0, 0); 523b4a3311eSnicm layout_make_leaf(lc, wp); 524baddd6b2Snicm layout_fix_panes(w, NULL); 525311827fbSnicm } 526311827fbSnicm 527af9e4c5dSnicm void 528af9e4c5dSnicm layout_free(struct window *w) 529af9e4c5dSnicm { 530af9e4c5dSnicm layout_free_cell(w->layout_root); 531af9e4c5dSnicm } 532af9e4c5dSnicm 533af9e4c5dSnicm /* Resize the entire layout after window resize. */ 534af9e4c5dSnicm void 535af9e4c5dSnicm layout_resize(struct window *w, u_int sx, u_int sy) 536af9e4c5dSnicm { 537af9e4c5dSnicm struct layout_cell *lc = w->layout_root; 538af9e4c5dSnicm int xlimit, ylimit, xchange, ychange; 539af9e4c5dSnicm 540af9e4c5dSnicm /* 541af9e4c5dSnicm * Adjust horizontally. Do not attempt to reduce the layout lower than 542af9e4c5dSnicm * the minimum (more than the amount returned by layout_resize_check). 543af9e4c5dSnicm * 544af9e4c5dSnicm * This can mean that the window size is smaller than the total layout 545af9e4c5dSnicm * size: redrawing this is handled at a higher level, but it does leave 546af9e4c5dSnicm * a problem with growing the window size here: if the current size is 547af9e4c5dSnicm * < the minimum, growing proportionately by adding to each pane is 548af9e4c5dSnicm * wrong as it would keep the layout size larger than the window size. 549af9e4c5dSnicm * Instead, spread the difference between the minimum and the new size 550af9e4c5dSnicm * out proportionately - this should leave the layout fitting the new 551af9e4c5dSnicm * window size. 552af9e4c5dSnicm */ 553d4ddf7e1Snicm xchange = sx - lc->sx; 55407b91187Snicm xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT); 555af9e4c5dSnicm if (xchange < 0 && xchange < -xlimit) 556af9e4c5dSnicm xchange = -xlimit; 557af9e4c5dSnicm if (xlimit == 0) { 558af9e4c5dSnicm if (sx <= lc->sx) /* lc->sx is minimum possible */ 559af9e4c5dSnicm xchange = 0; 560311827fbSnicm else 561af9e4c5dSnicm xchange = sx - lc->sx; 562af9e4c5dSnicm } 563af9e4c5dSnicm if (xchange != 0) 56407b91187Snicm layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, xchange); 565311827fbSnicm 566af9e4c5dSnicm /* Adjust vertically in a similar fashion. */ 567d4ddf7e1Snicm ychange = sy - lc->sy; 56807b91187Snicm ylimit = layout_resize_check(w, lc, LAYOUT_TOPBOTTOM); 569af9e4c5dSnicm if (ychange < 0 && ychange < -ylimit) 570af9e4c5dSnicm ychange = -ylimit; 571af9e4c5dSnicm if (ylimit == 0) { 572af9e4c5dSnicm if (sy <= lc->sy) /* lc->sy is minimum possible */ 573af9e4c5dSnicm ychange = 0; 574af9e4c5dSnicm else 575af9e4c5dSnicm ychange = sy - lc->sy; 576af9e4c5dSnicm } 577af9e4c5dSnicm if (ychange != 0) 57807b91187Snicm layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange); 579af9e4c5dSnicm 580af9e4c5dSnicm /* Fix cell offsets. */ 58142fbd26aSnicm layout_fix_offsets(w); 582baddd6b2Snicm layout_fix_panes(w, NULL); 583311827fbSnicm } 584311827fbSnicm 5853cfcdd36Snicm /* Resize a pane to an absolute size. */ 5863cfcdd36Snicm void 5873cfcdd36Snicm layout_resize_pane_to(struct window_pane *wp, enum layout_type type, 5883cfcdd36Snicm u_int new_size) 5893cfcdd36Snicm { 5903cfcdd36Snicm struct layout_cell *lc, *lcparent; 5913cfcdd36Snicm int change, size; 5923cfcdd36Snicm 5933cfcdd36Snicm lc = wp->layout_cell; 5943cfcdd36Snicm 5953cfcdd36Snicm /* Find next parent of the same type. */ 5963cfcdd36Snicm lcparent = lc->parent; 5973cfcdd36Snicm while (lcparent != NULL && lcparent->type != type) { 5983cfcdd36Snicm lc = lcparent; 5993cfcdd36Snicm lcparent = lc->parent; 6003cfcdd36Snicm } 6013cfcdd36Snicm if (lcparent == NULL) 6023cfcdd36Snicm return; 6033cfcdd36Snicm 6043cfcdd36Snicm /* Work out the size adjustment. */ 6053cfcdd36Snicm if (type == LAYOUT_LEFTRIGHT) 6063cfcdd36Snicm size = lc->sx; 6073cfcdd36Snicm else 6083cfcdd36Snicm size = lc->sy; 6093cfcdd36Snicm if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) 6103cfcdd36Snicm change = size - new_size; 6113cfcdd36Snicm else 6123cfcdd36Snicm change = new_size - size; 6133cfcdd36Snicm 6143cfcdd36Snicm /* Resize the pane. */ 615f4d5873aSnicm layout_resize_pane(wp, type, change, 1); 6163cfcdd36Snicm } 6173cfcdd36Snicm 618af9e4c5dSnicm void 6193d1607c5Snicm layout_resize_layout(struct window *w, struct layout_cell *lc, 6203d1607c5Snicm enum layout_type type, int change, int opposite) 621af9e4c5dSnicm { 622af9e4c5dSnicm int needed, size; 623af9e4c5dSnicm 624af9e4c5dSnicm /* Grow or shrink the cell. */ 625af9e4c5dSnicm needed = change; 626af9e4c5dSnicm while (needed != 0) { 627af9e4c5dSnicm if (change > 0) { 628f4d5873aSnicm size = layout_resize_pane_grow(w, lc, type, needed, 629f4d5873aSnicm opposite); 630af9e4c5dSnicm needed -= size; 631af9e4c5dSnicm } else { 63207b91187Snicm size = layout_resize_pane_shrink(w, lc, type, needed); 633af9e4c5dSnicm needed += size; 634af9e4c5dSnicm } 635af9e4c5dSnicm 636af9e4c5dSnicm if (size == 0) /* no more change possible */ 637311827fbSnicm break; 638311827fbSnicm } 639af9e4c5dSnicm 640af9e4c5dSnicm /* Fix cell offsets. */ 64142fbd26aSnicm layout_fix_offsets(w); 642baddd6b2Snicm layout_fix_panes(w, NULL); 6433d1607c5Snicm notify_window("window-layout-changed", w); 6443d1607c5Snicm } 6453d1607c5Snicm 6463d1607c5Snicm /* Resize a single pane within the layout. */ 6473d1607c5Snicm void 6483d1607c5Snicm layout_resize_pane(struct window_pane *wp, enum layout_type type, int change, 6493d1607c5Snicm int opposite) 6503d1607c5Snicm { 6513d1607c5Snicm struct layout_cell *lc, *lcparent; 6523d1607c5Snicm 6533d1607c5Snicm lc = wp->layout_cell; 6543d1607c5Snicm 6553d1607c5Snicm /* Find next parent of the same type. */ 6563d1607c5Snicm lcparent = lc->parent; 6573d1607c5Snicm while (lcparent != NULL && lcparent->type != type) { 6583d1607c5Snicm lc = lcparent; 6593d1607c5Snicm lcparent = lc->parent; 6603d1607c5Snicm } 6613d1607c5Snicm if (lcparent == NULL) 6623d1607c5Snicm return; 6633d1607c5Snicm 6643d1607c5Snicm /* If this is the last cell, move back one. */ 6653d1607c5Snicm if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) 6663d1607c5Snicm lc = TAILQ_PREV(lc, layout_cells, entry); 6673d1607c5Snicm 6683d1607c5Snicm layout_resize_layout(wp->window, lc, type, change, opposite); 669311827fbSnicm } 670af9e4c5dSnicm 6713cfcdd36Snicm /* Helper function to grow pane. */ 672bc3b19faSnicm static int 67307b91187Snicm layout_resize_pane_grow(struct window *w, struct layout_cell *lc, 674f4d5873aSnicm enum layout_type type, int needed, int opposite) 675af9e4c5dSnicm { 676af9e4c5dSnicm struct layout_cell *lcadd, *lcremove; 677f4d5873aSnicm u_int size = 0; 678af9e4c5dSnicm 679af9e4c5dSnicm /* Growing. Always add to the current cell. */ 680af9e4c5dSnicm lcadd = lc; 681af9e4c5dSnicm 682af9e4c5dSnicm /* Look towards the tail for a suitable cell for reduction. */ 683af9e4c5dSnicm lcremove = TAILQ_NEXT(lc, entry); 684af9e4c5dSnicm while (lcremove != NULL) { 68507b91187Snicm size = layout_resize_check(w, lcremove, type); 686af9e4c5dSnicm if (size > 0) 687af9e4c5dSnicm break; 688af9e4c5dSnicm lcremove = TAILQ_NEXT(lcremove, entry); 689af9e4c5dSnicm } 690af9e4c5dSnicm 691af9e4c5dSnicm /* If none found, look towards the head. */ 692f4d5873aSnicm if (opposite && lcremove == NULL) { 693af9e4c5dSnicm lcremove = TAILQ_PREV(lc, layout_cells, entry); 694af9e4c5dSnicm while (lcremove != NULL) { 69507b91187Snicm size = layout_resize_check(w, lcremove, type); 696af9e4c5dSnicm if (size > 0) 697af9e4c5dSnicm break; 698af9e4c5dSnicm lcremove = TAILQ_PREV(lcremove, layout_cells, entry); 699af9e4c5dSnicm } 700f4d5873aSnicm } 701af9e4c5dSnicm if (lcremove == NULL) 702af9e4c5dSnicm return (0); 703af9e4c5dSnicm 704af9e4c5dSnicm /* Change the cells. */ 705af9e4c5dSnicm if (size > (u_int) needed) 706af9e4c5dSnicm size = needed; 70707b91187Snicm layout_resize_adjust(w, lcadd, type, size); 70807b91187Snicm layout_resize_adjust(w, lcremove, type, -size); 709af9e4c5dSnicm return (size); 710af9e4c5dSnicm } 711af9e4c5dSnicm 7123cfcdd36Snicm /* Helper function to shrink pane. */ 713bc3b19faSnicm static int 71407b91187Snicm layout_resize_pane_shrink(struct window *w, struct layout_cell *lc, 71507b91187Snicm enum layout_type type, int needed) 716af9e4c5dSnicm { 717af9e4c5dSnicm struct layout_cell *lcadd, *lcremove; 718af9e4c5dSnicm u_int size; 719af9e4c5dSnicm 720af9e4c5dSnicm /* Shrinking. Find cell to remove from by walking towards head. */ 721af9e4c5dSnicm lcremove = lc; 722af9e4c5dSnicm do { 72307b91187Snicm size = layout_resize_check(w, lcremove, type); 724af9e4c5dSnicm if (size != 0) 725af9e4c5dSnicm break; 726af9e4c5dSnicm lcremove = TAILQ_PREV(lcremove, layout_cells, entry); 727af9e4c5dSnicm } while (lcremove != NULL); 728af9e4c5dSnicm if (lcremove == NULL) 729af9e4c5dSnicm return (0); 730af9e4c5dSnicm 731af9e4c5dSnicm /* And add onto the next cell (from the original cell). */ 732af9e4c5dSnicm lcadd = TAILQ_NEXT(lc, entry); 733af9e4c5dSnicm if (lcadd == NULL) 734af9e4c5dSnicm return (0); 735af9e4c5dSnicm 736af9e4c5dSnicm /* Change the cells. */ 737af9e4c5dSnicm if (size > (u_int) -needed) 738af9e4c5dSnicm size = -needed; 73907b91187Snicm layout_resize_adjust(w, lcadd, type, size); 74007b91187Snicm layout_resize_adjust(w, lcremove, type, -size); 741af9e4c5dSnicm return (size); 742af9e4c5dSnicm } 743af9e4c5dSnicm 744572cd943Snicm /* Assign window pane to newly split cell. */ 745572cd943Snicm void 746baddd6b2Snicm layout_assign_pane(struct layout_cell *lc, struct window_pane *wp, 747baddd6b2Snicm int do_not_resize) 748572cd943Snicm { 749572cd943Snicm layout_make_leaf(lc, wp); 750baddd6b2Snicm if (do_not_resize) 751baddd6b2Snicm layout_fix_panes(wp->window, wp); 752baddd6b2Snicm else 753baddd6b2Snicm layout_fix_panes(wp->window, NULL); 754572cd943Snicm } 755572cd943Snicm 756824e896bSnicm /* Calculate the new pane size for resized parent. */ 757824e896bSnicm static u_int 758824e896bSnicm layout_new_pane_size(struct window *w, u_int previous, struct layout_cell *lc, 759824e896bSnicm enum layout_type type, u_int size, u_int count_left, u_int size_left) 760824e896bSnicm { 761824e896bSnicm u_int new_size, min, max, available; 762824e896bSnicm 763824e896bSnicm /* If this is the last cell, it can take all of the remaining size. */ 764824e896bSnicm if (count_left == 1) 765824e896bSnicm return (size_left); 766824e896bSnicm 767824e896bSnicm /* How much is available in this parent? */ 768824e896bSnicm available = layout_resize_check(w, lc, type); 769824e896bSnicm 770824e896bSnicm /* 771824e896bSnicm * Work out the minimum size of this cell and the new size 772824e896bSnicm * proportionate to the previous size. 773824e896bSnicm */ 774824e896bSnicm min = (PANE_MINIMUM + 1) * (count_left - 1); 775824e896bSnicm if (type == LAYOUT_LEFTRIGHT) { 776824e896bSnicm if (lc->sx - available > min) 777824e896bSnicm min = lc->sx - available; 778824e896bSnicm new_size = (lc->sx * size) / previous; 779824e896bSnicm } else { 780824e896bSnicm if (lc->sy - available > min) 781824e896bSnicm min = lc->sy - available; 782824e896bSnicm new_size = (lc->sy * size) / previous; 783824e896bSnicm } 784824e896bSnicm 785824e896bSnicm /* Check against the maximum and minimum size. */ 786824e896bSnicm max = size_left - min; 787824e896bSnicm if (new_size > max) 788824e896bSnicm new_size = max; 789824e896bSnicm if (new_size < PANE_MINIMUM) 790824e896bSnicm new_size = PANE_MINIMUM; 791824e896bSnicm return (new_size); 792824e896bSnicm } 793824e896bSnicm 794824e896bSnicm /* Check if the cell and all its children can be resized to a specific size. */ 795824e896bSnicm static int 796824e896bSnicm layout_set_size_check(struct window *w, struct layout_cell *lc, 797824e896bSnicm enum layout_type type, int size) 798824e896bSnicm { 799824e896bSnicm struct layout_cell *lcchild; 800824e896bSnicm u_int new_size, available, previous, count, idx; 801824e896bSnicm 802824e896bSnicm /* Cells with no children must just be bigger than minimum. */ 803824e896bSnicm if (lc->type == LAYOUT_WINDOWPANE) 804824e896bSnicm return (size >= PANE_MINIMUM); 805824e896bSnicm available = size; 806824e896bSnicm 807824e896bSnicm /* Count number of children. */ 808824e896bSnicm count = 0; 809824e896bSnicm TAILQ_FOREACH(lcchild, &lc->cells, entry) 810824e896bSnicm count++; 811824e896bSnicm 812824e896bSnicm /* Check new size will work for each child. */ 813824e896bSnicm if (lc->type == type) { 814eb84caa4Snicm if (available < (count * 2) - 1) 815eb84caa4Snicm return (0); 816eb84caa4Snicm 817824e896bSnicm if (type == LAYOUT_LEFTRIGHT) 818824e896bSnicm previous = lc->sx; 819824e896bSnicm else 820824e896bSnicm previous = lc->sy; 821824e896bSnicm 822824e896bSnicm idx = 0; 823824e896bSnicm TAILQ_FOREACH(lcchild, &lc->cells, entry) { 824824e896bSnicm new_size = layout_new_pane_size(w, previous, lcchild, 825824e896bSnicm type, size, count - idx, available); 826eb84caa4Snicm if (idx == count - 1) { 827824e896bSnicm if (new_size > available) 828824e896bSnicm return (0); 829eb84caa4Snicm available -= new_size; 830eb84caa4Snicm } else { 831eb84caa4Snicm if (new_size + 1 > available) 832eb84caa4Snicm return (0); 833eb84caa4Snicm available -= new_size + 1; 834eb84caa4Snicm } 835824e896bSnicm if (!layout_set_size_check(w, lcchild, type, new_size)) 836824e896bSnicm return (0); 837824e896bSnicm idx++; 838824e896bSnicm } 839824e896bSnicm } else { 840824e896bSnicm TAILQ_FOREACH(lcchild, &lc->cells, entry) { 841824e896bSnicm if (lcchild->type == LAYOUT_WINDOWPANE) 842824e896bSnicm continue; 843824e896bSnicm if (!layout_set_size_check(w, lcchild, type, size)) 844824e896bSnicm return (0); 845824e896bSnicm } 846824e896bSnicm } 847824e896bSnicm 848824e896bSnicm return (1); 849824e896bSnicm } 850824e896bSnicm 851824e896bSnicm /* Resize all child cells to fit within the current cell. */ 852824e896bSnicm static void 853824e896bSnicm layout_resize_child_cells(struct window *w, struct layout_cell *lc) 854824e896bSnicm { 855824e896bSnicm struct layout_cell *lcchild; 856824e896bSnicm u_int previous, available, count, idx; 857824e896bSnicm 858824e896bSnicm if (lc->type == LAYOUT_WINDOWPANE) 859824e896bSnicm return; 860824e896bSnicm 861824e896bSnicm /* What is the current size used? */ 862824e896bSnicm count = 0; 863824e896bSnicm previous = 0; 864824e896bSnicm TAILQ_FOREACH(lcchild, &lc->cells, entry) { 865824e896bSnicm count++; 866824e896bSnicm if (lc->type == LAYOUT_LEFTRIGHT) 867824e896bSnicm previous += lcchild->sx; 868824e896bSnicm else if (lc->type == LAYOUT_TOPBOTTOM) 869824e896bSnicm previous += lcchild->sy; 870824e896bSnicm } 871824e896bSnicm previous += (count - 1); 872824e896bSnicm 873824e896bSnicm /* And how much is available? */ 874824e896bSnicm available = 0; 875824e896bSnicm if (lc->type == LAYOUT_LEFTRIGHT) 876824e896bSnicm available = lc->sx; 877824e896bSnicm else if (lc->type == LAYOUT_TOPBOTTOM) 878824e896bSnicm available = lc->sy; 879824e896bSnicm 880824e896bSnicm /* Resize children into the new size. */ 881824e896bSnicm idx = 0; 882824e896bSnicm TAILQ_FOREACH(lcchild, &lc->cells, entry) { 883824e896bSnicm if (lc->type == LAYOUT_TOPBOTTOM) { 884824e896bSnicm lcchild->sx = lc->sx; 885824e896bSnicm lcchild->xoff = lc->xoff; 886824e896bSnicm } else { 887824e896bSnicm lcchild->sx = layout_new_pane_size(w, previous, lcchild, 888824e896bSnicm lc->type, lc->sx, count - idx, available); 889824e896bSnicm available -= (lcchild->sx + 1); 890824e896bSnicm } 891824e896bSnicm if (lc->type == LAYOUT_LEFTRIGHT) 892824e896bSnicm lcchild->sy = lc->sy; 893824e896bSnicm else { 894824e896bSnicm lcchild->sy = layout_new_pane_size(w, previous, lcchild, 895824e896bSnicm lc->type, lc->sy, count - idx, available); 896824e896bSnicm available -= (lcchild->sy + 1); 897824e896bSnicm } 898824e896bSnicm layout_resize_child_cells(w, lcchild); 899824e896bSnicm idx++; 900824e896bSnicm } 901824e896bSnicm } 902824e896bSnicm 903572cd943Snicm /* 904572cd943Snicm * Split a pane into two. size is a hint, or -1 for default half/half 905572cd943Snicm * split. This must be followed by layout_assign_pane before much else happens! 906824e896bSnicm */ 907572cd943Snicm struct layout_cell * 908d5221700Snicm layout_split_pane(struct window_pane *wp, enum layout_type type, int size, 909c26c4f79Snicm int flags) 910af9e4c5dSnicm { 9119c03dbf0Snicm struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; 912dd0df669Snicm struct style *sb_style = &wp->scrollbar_style; 913e217846fSnicm u_int sx, sy, xoff, yoff, size1, size2, minimum; 914824e896bSnicm u_int new_size, saved_size, resize_first = 0; 915e217846fSnicm int full_size = (flags & SPAWN_FULLSIZE), status; 916d2117533Snicm int scrollbars; 917af9e4c5dSnicm 918824e896bSnicm /* 919824e896bSnicm * If full_size is specified, add a new cell at the top of the window 920824e896bSnicm * layout. Otherwise, split the cell for the current pane. 921824e896bSnicm */ 922824e896bSnicm if (full_size) 923824e896bSnicm lc = wp->window->layout_root; 924824e896bSnicm else 925af9e4c5dSnicm lc = wp->layout_cell; 926e217846fSnicm status = options_get_number(wp->window->options, "pane-border-status"); 927d2117533Snicm scrollbars = options_get_number(wp->window->options, "pane-scrollbars"); 928af9e4c5dSnicm 929af9e4c5dSnicm /* Copy the old cell size. */ 930af9e4c5dSnicm sx = lc->sx; 931af9e4c5dSnicm sy = lc->sy; 932af9e4c5dSnicm xoff = lc->xoff; 933af9e4c5dSnicm yoff = lc->yoff; 934af9e4c5dSnicm 935af9e4c5dSnicm /* Check there is enough space for the two new panes. */ 936af9e4c5dSnicm switch (type) { 937af9e4c5dSnicm case LAYOUT_LEFTRIGHT: 938dd0df669Snicm if (scrollbars) { 939dd0df669Snicm minimum = PANE_MINIMUM * 2 + sb_style->width + 940dd0df669Snicm sb_style->pad; 941dd0df669Snicm } else 942d2117533Snicm minimum = PANE_MINIMUM * 2 + 1; 943d2117533Snicm if (sx < minimum) 944572cd943Snicm return (NULL); 945af9e4c5dSnicm break; 946af9e4c5dSnicm case LAYOUT_TOPBOTTOM: 947d2117533Snicm if (layout_add_horizontal_border(wp->window, lc, status)) 9489bc2f29eSnicm minimum = PANE_MINIMUM * 2 + 2; 9499bc2f29eSnicm else 950e217846fSnicm minimum = PANE_MINIMUM * 2 + 1; 951e217846fSnicm if (sy < minimum) 952572cd943Snicm return (NULL); 953af9e4c5dSnicm break; 954af9e4c5dSnicm default: 955af9e4c5dSnicm fatalx("bad layout type"); 956af9e4c5dSnicm } 957af9e4c5dSnicm 958824e896bSnicm /* 959824e896bSnicm * Calculate new cell sizes. size is the target size or -1 for middle 960824e896bSnicm * split, size1 is the size of the top/left and size2 the bottom/right. 961824e896bSnicm */ 962824e896bSnicm if (type == LAYOUT_LEFTRIGHT) 963824e896bSnicm saved_size = sx; 964824e896bSnicm else 965824e896bSnicm saved_size = sy; 966824e896bSnicm if (size < 0) 967824e896bSnicm size2 = ((saved_size + 1) / 2) - 1; 968c26c4f79Snicm else if (flags & SPAWN_BEFORE) 969824e896bSnicm size2 = saved_size - size - 1; 970824e896bSnicm else 971824e896bSnicm size2 = size; 972824e896bSnicm if (size2 < PANE_MINIMUM) 973824e896bSnicm size2 = PANE_MINIMUM; 974824e896bSnicm else if (size2 > saved_size - 2) 975824e896bSnicm size2 = saved_size - 2; 976824e896bSnicm size1 = saved_size - 1 - size2; 977824e896bSnicm 978824e896bSnicm /* Which size are we using? */ 979c26c4f79Snicm if (flags & SPAWN_BEFORE) 980824e896bSnicm new_size = size2; 981824e896bSnicm else 982824e896bSnicm new_size = size1; 983824e896bSnicm 984824e896bSnicm /* Confirm there is enough space for full size pane. */ 985824e896bSnicm if (full_size && !layout_set_size_check(wp->window, lc, type, new_size)) 986824e896bSnicm return (NULL); 987824e896bSnicm 988af9e4c5dSnicm if (lc->parent != NULL && lc->parent->type == type) { 989af9e4c5dSnicm /* 990af9e4c5dSnicm * If the parent exists and is of the same type as the split, 991af9e4c5dSnicm * create a new cell and insert it after this one. 992af9e4c5dSnicm */ 9939c03dbf0Snicm lcparent = lc->parent; 9949c03dbf0Snicm lcnew = layout_create_cell(lcparent); 995c26c4f79Snicm if (flags & SPAWN_BEFORE) 9969c03dbf0Snicm TAILQ_INSERT_BEFORE(lc, lcnew, entry); 9979c03dbf0Snicm else 9989c03dbf0Snicm TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry); 999824e896bSnicm } else if (full_size && lc->parent == NULL && lc->type == type) { 1000824e896bSnicm /* 1001824e896bSnicm * If the new full size pane is the same type as the root 1002824e896bSnicm * split, insert the new pane under the existing root cell 1003824e896bSnicm * instead of creating a new root cell. The existing layout 1004824e896bSnicm * must be resized before inserting the new cell. 1005824e896bSnicm */ 1006824e896bSnicm if (lc->type == LAYOUT_LEFTRIGHT) { 1007824e896bSnicm lc->sx = new_size; 1008824e896bSnicm layout_resize_child_cells(wp->window, lc); 1009824e896bSnicm lc->sx = saved_size; 1010824e896bSnicm } else if (lc->type == LAYOUT_TOPBOTTOM) { 1011824e896bSnicm lc->sy = new_size; 1012824e896bSnicm layout_resize_child_cells(wp->window, lc); 1013824e896bSnicm lc->sy = saved_size; 1014824e896bSnicm } 1015824e896bSnicm resize_first = 1; 1016824e896bSnicm 1017824e896bSnicm /* Create the new cell. */ 1018824e896bSnicm lcnew = layout_create_cell(lc); 1019f9ebce2cSnicm size = saved_size - 1 - new_size; 1020824e896bSnicm if (lc->type == LAYOUT_LEFTRIGHT) 1021f9ebce2cSnicm layout_set_size(lcnew, size, sy, 0, 0); 1022824e896bSnicm else if (lc->type == LAYOUT_TOPBOTTOM) 1023f9ebce2cSnicm layout_set_size(lcnew, sx, size, 0, 0); 1024c26c4f79Snicm if (flags & SPAWN_BEFORE) 1025824e896bSnicm TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry); 1026824e896bSnicm else 1027824e896bSnicm TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); 1028af9e4c5dSnicm } else { 1029af9e4c5dSnicm /* 1030af9e4c5dSnicm * Otherwise create a new parent and insert it. 1031af9e4c5dSnicm */ 1032af9e4c5dSnicm 1033af9e4c5dSnicm /* Create and insert the replacement parent. */ 1034af9e4c5dSnicm lcparent = layout_create_cell(lc->parent); 1035af9e4c5dSnicm layout_make_node(lcparent, type); 1036af9e4c5dSnicm layout_set_size(lcparent, sx, sy, xoff, yoff); 1037af9e4c5dSnicm if (lc->parent == NULL) 1038af9e4c5dSnicm wp->window->layout_root = lcparent; 1039af9e4c5dSnicm else 1040af9e4c5dSnicm TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry); 1041af9e4c5dSnicm 1042af9e4c5dSnicm /* Insert the old cell. */ 1043af9e4c5dSnicm lc->parent = lcparent; 1044af9e4c5dSnicm TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry); 1045af9e4c5dSnicm 1046af9e4c5dSnicm /* Create the new child cell. */ 1047af9e4c5dSnicm lcnew = layout_create_cell(lcparent); 1048c26c4f79Snicm if (flags & SPAWN_BEFORE) 10499c03dbf0Snicm TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry); 10509c03dbf0Snicm else 1051af9e4c5dSnicm TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry); 1052af9e4c5dSnicm } 1053c26c4f79Snicm if (flags & SPAWN_BEFORE) { 10549c03dbf0Snicm lc1 = lcnew; 10559c03dbf0Snicm lc2 = lc; 10569c03dbf0Snicm } else { 10579c03dbf0Snicm lc1 = lc; 10589c03dbf0Snicm lc2 = lcnew; 10599c03dbf0Snicm } 1060af9e4c5dSnicm 1061824e896bSnicm /* 1062824e896bSnicm * Set new cell sizes. size1 is the size of the top/left and size2 the 1063824e896bSnicm * bottom/right. 1064af9e4c5dSnicm */ 1065824e896bSnicm if (!resize_first && type == LAYOUT_LEFTRIGHT) { 10669c03dbf0Snicm layout_set_size(lc1, size1, sy, xoff, yoff); 10679c03dbf0Snicm layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff); 1068824e896bSnicm } else if (!resize_first && type == LAYOUT_TOPBOTTOM) { 10699c03dbf0Snicm layout_set_size(lc1, sx, size1, xoff, yoff); 10709c03dbf0Snicm layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1); 1071af9e4c5dSnicm } 1072824e896bSnicm if (full_size) { 1073824e896bSnicm if (!resize_first) 1074824e896bSnicm layout_resize_child_cells(wp->window, lc); 107542fbd26aSnicm layout_fix_offsets(wp->window); 1076824e896bSnicm } else 1077af9e4c5dSnicm layout_make_leaf(lc, wp); 1078af9e4c5dSnicm 1079572cd943Snicm return (lcnew); 1080af9e4c5dSnicm } 1081af9e4c5dSnicm 1082f4611a41Snicm /* Destroy the cell associated with a pane. */ 1083af9e4c5dSnicm void 1084af9e4c5dSnicm layout_close_pane(struct window_pane *wp) 1085af9e4c5dSnicm { 108607b91187Snicm struct window *w = wp->window; 108707b91187Snicm 1088f4611a41Snicm /* Remove the cell. */ 108907b91187Snicm layout_destroy_cell(w, wp->layout_cell, &w->layout_root); 1090af9e4c5dSnicm 1091af9e4c5dSnicm /* Fix pane offsets and sizes. */ 109207b91187Snicm if (w->layout_root != NULL) { 109342fbd26aSnicm layout_fix_offsets(w); 1094baddd6b2Snicm layout_fix_panes(w, NULL); 1095af9e4c5dSnicm } 10962ae124feSnicm notify_window("window-layout-changed", w); 1097f4611a41Snicm } 1098967ee5b9Snicm 1099967ee5b9Snicm int 1100967ee5b9Snicm layout_spread_cell(struct window *w, struct layout_cell *parent) 1101967ee5b9Snicm { 1102967ee5b9Snicm struct layout_cell *lc; 1103dd0df669Snicm struct style *sb_style = &w->active->scrollbar_style; 1104*5df986e2Snicm u_int number, each, size, this, remainder; 1105d2117533Snicm int change, changed, status, scrollbars; 1106967ee5b9Snicm 1107967ee5b9Snicm number = 0; 1108967ee5b9Snicm TAILQ_FOREACH (lc, &parent->cells, entry) 1109967ee5b9Snicm number++; 1110967ee5b9Snicm if (number <= 1) 1111967ee5b9Snicm return (0); 1112e217846fSnicm status = options_get_number(w->options, "pane-border-status"); 1113d2117533Snicm scrollbars = options_get_number(w->options, "pane-scrollbars"); 1114967ee5b9Snicm 1115d2117533Snicm if (parent->type == LAYOUT_LEFTRIGHT) { 1116d2117533Snicm if (scrollbars) 1117dd0df669Snicm size = parent->sx - sb_style->width + sb_style->pad; 1118d2117533Snicm else 1119967ee5b9Snicm size = parent->sx; 1120d2117533Snicm } 1121e217846fSnicm else if (parent->type == LAYOUT_TOPBOTTOM) { 1122d2117533Snicm if (layout_add_horizontal_border(w, parent, status)) 11239bc2f29eSnicm size = parent->sy - 1; 11249bc2f29eSnicm else 1125967ee5b9Snicm size = parent->sy; 1126e217846fSnicm } else 1127e217846fSnicm return (0); 1128e217846fSnicm if (size < number - 1) 1129967ee5b9Snicm return (0); 1130967ee5b9Snicm each = (size - (number - 1)) / number; 1131e217846fSnicm if (each == 0) 1132e217846fSnicm return (0); 1133*5df986e2Snicm /* 1134*5df986e2Snicm * Remaining space after assigning that which can be evenly 1135*5df986e2Snicm * distributed. 1136*5df986e2Snicm */ 1137*5df986e2Snicm remainder = size - (number * (each + 1)) + 1; 1138967ee5b9Snicm 1139967ee5b9Snicm changed = 0; 1140967ee5b9Snicm TAILQ_FOREACH (lc, &parent->cells, entry) { 1141967ee5b9Snicm change = 0; 1142967ee5b9Snicm if (parent->type == LAYOUT_LEFTRIGHT) { 1143967ee5b9Snicm change = each - (int)lc->sx; 1144*5df986e2Snicm if (remainder > 0) { 1145*5df986e2Snicm change++; 1146*5df986e2Snicm remainder--; 1147*5df986e2Snicm } 1148967ee5b9Snicm layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change); 1149967ee5b9Snicm } else if (parent->type == LAYOUT_TOPBOTTOM) { 1150d2117533Snicm if (layout_add_horizontal_border(w, lc, status)) 11519bc2f29eSnicm this = each + 1; 11529bc2f29eSnicm else 1153e217846fSnicm this = each; 1154*5df986e2Snicm if (remainder > 0) { 1155*5df986e2Snicm this++; 1156*5df986e2Snicm remainder--; 1157*5df986e2Snicm } 1158e217846fSnicm change = this - (int)lc->sy; 1159967ee5b9Snicm layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change); 1160967ee5b9Snicm } 1161967ee5b9Snicm if (change != 0) 1162967ee5b9Snicm changed = 1; 1163967ee5b9Snicm } 1164967ee5b9Snicm return (changed); 1165967ee5b9Snicm } 1166967ee5b9Snicm 1167967ee5b9Snicm void 1168967ee5b9Snicm layout_spread_out(struct window_pane *wp) 1169967ee5b9Snicm { 1170967ee5b9Snicm struct layout_cell *parent; 1171967ee5b9Snicm struct window *w = wp->window; 1172967ee5b9Snicm 1173967ee5b9Snicm parent = wp->layout_cell->parent; 1174967ee5b9Snicm if (parent == NULL) 1175967ee5b9Snicm return; 1176967ee5b9Snicm 1177967ee5b9Snicm do { 1178967ee5b9Snicm if (layout_spread_cell(w, parent)) { 117942fbd26aSnicm layout_fix_offsets(w); 1180baddd6b2Snicm layout_fix_panes(w, NULL); 1181967ee5b9Snicm break; 1182967ee5b9Snicm } 1183967ee5b9Snicm } while ((parent = parent->parent) != NULL); 1184967ee5b9Snicm } 1185