1 /* $OpenBSD: layout.c,v 1.28 2016/08/03 09:07:02 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <stdlib.h> 22 23 #include "tmux.h" 24 25 /* 26 * The window layout is a tree of cells each of which can be one of: a 27 * left-right container for a list of cells, a top-bottom container for a list 28 * of cells, or a container for a window pane. 29 * 30 * Each window has a pointer to the root of its layout tree (containing its 31 * panes), every pane has a pointer back to the cell containing it, and each 32 * cell a pointer to its parent cell. 33 */ 34 35 static u_int layout_resize_check(struct window *, struct layout_cell *, 36 enum layout_type); 37 static int layout_resize_pane_grow(struct window *, struct layout_cell *, 38 enum layout_type, int); 39 static int layout_resize_pane_shrink(struct window *, struct layout_cell *, 40 enum layout_type, int); 41 static int layout_need_status(struct layout_cell *, int); 42 43 struct layout_cell * 44 layout_create_cell(struct layout_cell *lcparent) 45 { 46 struct layout_cell *lc; 47 48 lc = xmalloc(sizeof *lc); 49 lc->type = LAYOUT_WINDOWPANE; 50 lc->parent = lcparent; 51 52 TAILQ_INIT(&lc->cells); 53 54 lc->sx = UINT_MAX; 55 lc->sy = UINT_MAX; 56 57 lc->xoff = UINT_MAX; 58 lc->yoff = UINT_MAX; 59 60 lc->wp = NULL; 61 62 return (lc); 63 } 64 65 void 66 layout_free_cell(struct layout_cell *lc) 67 { 68 struct layout_cell *lcchild; 69 70 switch (lc->type) { 71 case LAYOUT_LEFTRIGHT: 72 case LAYOUT_TOPBOTTOM: 73 while (!TAILQ_EMPTY(&lc->cells)) { 74 lcchild = TAILQ_FIRST(&lc->cells); 75 TAILQ_REMOVE(&lc->cells, lcchild, entry); 76 layout_free_cell(lcchild); 77 } 78 break; 79 case LAYOUT_WINDOWPANE: 80 if (lc->wp != NULL) 81 lc->wp->layout_cell = NULL; 82 break; 83 } 84 85 free(lc); 86 } 87 88 void 89 layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) 90 { 91 struct layout_cell *lcchild; 92 93 log_debug("%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, 94 " ", lc, lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, 95 lc->sy); 96 switch (lc->type) { 97 case LAYOUT_LEFTRIGHT: 98 case LAYOUT_TOPBOTTOM: 99 TAILQ_FOREACH(lcchild, &lc->cells, entry) 100 layout_print_cell(lcchild, hdr, n + 1); 101 break; 102 case LAYOUT_WINDOWPANE: 103 break; 104 } 105 } 106 107 void 108 layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, 109 u_int yoff) 110 { 111 lc->sx = sx; 112 lc->sy = sy; 113 114 lc->xoff = xoff; 115 lc->yoff = yoff; 116 } 117 118 void 119 layout_make_leaf(struct layout_cell *lc, struct window_pane *wp) 120 { 121 lc->type = LAYOUT_WINDOWPANE; 122 123 TAILQ_INIT(&lc->cells); 124 125 wp->layout_cell = lc; 126 lc->wp = wp; 127 } 128 129 void 130 layout_make_node(struct layout_cell *lc, enum layout_type type) 131 { 132 if (type == LAYOUT_WINDOWPANE) 133 fatalx("bad layout type"); 134 lc->type = type; 135 136 TAILQ_INIT(&lc->cells); 137 138 if (lc->wp != NULL) 139 lc->wp->layout_cell = NULL; 140 lc->wp = NULL; 141 } 142 143 /* Fix cell offsets based on their sizes. */ 144 void 145 layout_fix_offsets(struct layout_cell *lc) 146 { 147 struct layout_cell *lcchild; 148 u_int xoff, yoff; 149 150 if (lc->type == LAYOUT_LEFTRIGHT) { 151 xoff = lc->xoff; 152 TAILQ_FOREACH(lcchild, &lc->cells, entry) { 153 lcchild->xoff = xoff; 154 lcchild->yoff = lc->yoff; 155 if (lcchild->type != LAYOUT_WINDOWPANE) 156 layout_fix_offsets(lcchild); 157 xoff += lcchild->sx + 1; 158 } 159 } else { 160 yoff = lc->yoff; 161 TAILQ_FOREACH(lcchild, &lc->cells, entry) { 162 lcchild->xoff = lc->xoff; 163 lcchild->yoff = yoff; 164 if (lcchild->type != LAYOUT_WINDOWPANE) 165 layout_fix_offsets(lcchild); 166 yoff += lcchild->sy + 1; 167 } 168 } 169 } 170 171 /* 172 * Returns 1 if we need to reserve space for the pane status line. This is the 173 * case for the most upper panes only. 174 */ 175 static int 176 layout_need_status(struct layout_cell *lc, int at_top) 177 { 178 struct layout_cell *first_lc; 179 180 if (lc->parent) { 181 if (lc->parent->type == LAYOUT_LEFTRIGHT) 182 return (layout_need_status(lc->parent, at_top)); 183 184 if (at_top) 185 first_lc = TAILQ_FIRST(&lc->parent->cells); 186 else 187 first_lc = TAILQ_LAST(&lc->parent->cells,layout_cells); 188 if (lc == first_lc) 189 return (layout_need_status(lc->parent, at_top)); 190 return (0); 191 } 192 return (1); 193 } 194 195 /* Update pane offsets and sizes based on their cells. */ 196 void 197 layout_fix_panes(struct window *w, u_int wsx, u_int wsy) 198 { 199 struct window_pane *wp; 200 struct layout_cell *lc; 201 u_int sx, sy; 202 int shift, status, at_top; 203 204 status = options_get_number(w->options, "pane-border-status"); 205 at_top = (status == 1); 206 TAILQ_FOREACH(wp, &w->panes, entry) { 207 if ((lc = wp->layout_cell) == NULL) 208 continue; 209 210 if (status != 0) 211 shift = layout_need_status(lc, at_top); 212 else 213 shift = 0; 214 215 wp->xoff = lc->xoff; 216 wp->yoff = lc->yoff; 217 218 if (shift && at_top) 219 wp->yoff += 1; 220 221 /* 222 * Layout cells are limited by the smallest size of other cells 223 * within the same row or column; if this isn't the case 224 * resizing becomes difficult. 225 * 226 * However, panes do not have to take up their entire cell, so 227 * they can be cropped to the window edge if the layout 228 * overflows and they are partly visible. 229 * 230 * This stops cells being hidden unnecessarily. 231 */ 232 233 /* 234 * Work out the horizontal size. If the pane is actually 235 * outside the window or the entire pane is already visible, 236 * don't crop. 237 */ 238 if (lc->xoff >= wsx || lc->xoff + lc->sx < wsx) 239 sx = lc->sx; 240 else { 241 sx = wsx - lc->xoff; 242 if (sx < 1) 243 sx = lc->sx; 244 } 245 246 /* 247 * Similarly for the vertical size; the minimum vertical size 248 * is two because scroll regions cannot be one line. 249 */ 250 if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy) 251 sy = lc->sy; 252 else { 253 sy = wsy - lc->yoff; 254 if (sy < 2) 255 sy = lc->sy; 256 } 257 258 if (shift) 259 sy -= 1; 260 261 window_pane_resize(wp, sx, sy); 262 } 263 } 264 265 /* Count the number of available cells in a layout. */ 266 u_int 267 layout_count_cells(struct layout_cell *lc) 268 { 269 struct layout_cell *lcchild; 270 u_int n; 271 272 switch (lc->type) { 273 case LAYOUT_WINDOWPANE: 274 return (1); 275 case LAYOUT_LEFTRIGHT: 276 case LAYOUT_TOPBOTTOM: 277 n = 0; 278 TAILQ_FOREACH(lcchild, &lc->cells, entry) 279 n += layout_count_cells(lcchild); 280 return (n); 281 default: 282 fatalx("bad layout type"); 283 } 284 } 285 286 /* Calculate how much size is available to be removed from a cell. */ 287 static u_int 288 layout_resize_check(struct window *w, struct layout_cell *lc, 289 enum layout_type type) 290 { 291 struct layout_cell *lcchild; 292 u_int available, minimum; 293 294 if (lc->type == LAYOUT_WINDOWPANE) { 295 /* Space available in this cell only. */ 296 minimum = PANE_MINIMUM; 297 if (type == LAYOUT_LEFTRIGHT) 298 available = lc->sx; 299 else { 300 available = lc->sy; 301 minimum += layout_need_status(lc, 302 options_get_number(w->options, 303 "pane-border-status") == 1); 304 } 305 if (available > minimum) 306 available -= minimum; 307 else 308 available = 0; 309 } else if (lc->type == type) { 310 /* Same type: total of available space in all child cells. */ 311 available = 0; 312 TAILQ_FOREACH(lcchild, &lc->cells, entry) 313 available += layout_resize_check(w, lcchild, type); 314 } else { 315 /* Different type: minimum of available space in child cells. */ 316 minimum = UINT_MAX; 317 TAILQ_FOREACH(lcchild, &lc->cells, entry) { 318 available = layout_resize_check(w, lcchild, type); 319 if (available < minimum) 320 minimum = available; 321 } 322 available = minimum; 323 } 324 325 return (available); 326 } 327 328 /* 329 * Adjust cell size evenly, including altering its children. This function 330 * expects the change to have already been bounded to the space available. 331 */ 332 void 333 layout_resize_adjust(struct window *w, struct layout_cell *lc, 334 enum layout_type type, int change) 335 { 336 struct layout_cell *lcchild; 337 338 /* Adjust the cell size. */ 339 if (type == LAYOUT_LEFTRIGHT) 340 lc->sx += change; 341 else 342 lc->sy += change; 343 344 /* If this is a leaf cell, that is all that is necessary. */ 345 if (type == LAYOUT_WINDOWPANE) 346 return; 347 348 /* Child cell runs in a different direction. */ 349 if (lc->type != type) { 350 TAILQ_FOREACH(lcchild, &lc->cells, entry) 351 layout_resize_adjust(w, lcchild, type, change); 352 return; 353 } 354 355 /* 356 * Child cell runs in the same direction. Adjust each child equally 357 * until no further change is possible. 358 */ 359 while (change != 0) { 360 TAILQ_FOREACH(lcchild, &lc->cells, entry) { 361 if (change == 0) 362 break; 363 if (change > 0) { 364 layout_resize_adjust(w, lcchild, type, 1); 365 change--; 366 continue; 367 } 368 if (layout_resize_check(w, lcchild, type) > 0) { 369 layout_resize_adjust(w, lcchild, type, -1); 370 change++; 371 } 372 } 373 } 374 } 375 376 /* Destroy a cell and redistribute the space. */ 377 void 378 layout_destroy_cell(struct window *w, struct layout_cell *lc, 379 struct layout_cell **lcroot) 380 { 381 struct layout_cell *lcother, *lcparent; 382 383 /* 384 * If no parent, this is the last pane so window close is imminent and 385 * there is no need to resize anything. 386 */ 387 lcparent = lc->parent; 388 if (lcparent == NULL) { 389 layout_free_cell(lc); 390 *lcroot = NULL; 391 return; 392 } 393 394 /* Merge the space into the previous or next cell. */ 395 if (lc == TAILQ_FIRST(&lcparent->cells)) 396 lcother = TAILQ_NEXT(lc, entry); 397 else 398 lcother = TAILQ_PREV(lc, layout_cells, entry); 399 if (lcparent->type == LAYOUT_LEFTRIGHT) 400 layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1); 401 else 402 layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1); 403 404 /* Remove this from the parent's list. */ 405 TAILQ_REMOVE(&lcparent->cells, lc, entry); 406 layout_free_cell(lc); 407 408 /* 409 * If the parent now has one cell, remove the parent from the tree and 410 * replace it by that cell. 411 */ 412 lc = TAILQ_FIRST(&lcparent->cells); 413 if (TAILQ_NEXT(lc, entry) == NULL) { 414 TAILQ_REMOVE(&lcparent->cells, lc, entry); 415 416 lc->parent = lcparent->parent; 417 if (lc->parent == NULL) { 418 lc->xoff = 0; lc->yoff = 0; 419 *lcroot = lc; 420 } else 421 TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry); 422 423 layout_free_cell(lcparent); 424 } 425 } 426 427 void 428 layout_init(struct window *w, struct window_pane *wp) 429 { 430 struct layout_cell *lc; 431 432 lc = w->layout_root = layout_create_cell(NULL); 433 layout_set_size(lc, w->sx, w->sy, 0, 0); 434 layout_make_leaf(lc, wp); 435 436 layout_fix_panes(w, w->sx, w->sy); 437 } 438 439 void 440 layout_free(struct window *w) 441 { 442 layout_free_cell(w->layout_root); 443 } 444 445 /* Resize the entire layout after window resize. */ 446 void 447 layout_resize(struct window *w, u_int sx, u_int sy) 448 { 449 struct layout_cell *lc = w->layout_root; 450 int xlimit, ylimit, xchange, ychange; 451 452 /* 453 * Adjust horizontally. Do not attempt to reduce the layout lower than 454 * the minimum (more than the amount returned by layout_resize_check). 455 * 456 * This can mean that the window size is smaller than the total layout 457 * size: redrawing this is handled at a higher level, but it does leave 458 * a problem with growing the window size here: if the current size is 459 * < the minimum, growing proportionately by adding to each pane is 460 * wrong as it would keep the layout size larger than the window size. 461 * Instead, spread the difference between the minimum and the new size 462 * out proportionately - this should leave the layout fitting the new 463 * window size. 464 */ 465 xchange = sx - w->sx; 466 xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT); 467 if (xchange < 0 && xchange < -xlimit) 468 xchange = -xlimit; 469 if (xlimit == 0) { 470 if (sx <= lc->sx) /* lc->sx is minimum possible */ 471 xchange = 0; 472 else 473 xchange = sx - lc->sx; 474 } 475 if (xchange != 0) 476 layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, xchange); 477 478 /* Adjust vertically in a similar fashion. */ 479 ychange = sy - w->sy; 480 ylimit = layout_resize_check(w, lc, LAYOUT_TOPBOTTOM); 481 if (ychange < 0 && ychange < -ylimit) 482 ychange = -ylimit; 483 if (ylimit == 0) { 484 if (sy <= lc->sy) /* lc->sy is minimum possible */ 485 ychange = 0; 486 else 487 ychange = sy - lc->sy; 488 } 489 if (ychange != 0) 490 layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange); 491 492 /* Fix cell offsets. */ 493 layout_fix_offsets(lc); 494 layout_fix_panes(w, sx, sy); 495 } 496 497 /* Resize a pane to an absolute size. */ 498 void 499 layout_resize_pane_to(struct window_pane *wp, enum layout_type type, 500 u_int new_size) 501 { 502 struct layout_cell *lc, *lcparent; 503 int change, size; 504 505 lc = wp->layout_cell; 506 507 /* Find next parent of the same type. */ 508 lcparent = lc->parent; 509 while (lcparent != NULL && lcparent->type != type) { 510 lc = lcparent; 511 lcparent = lc->parent; 512 } 513 if (lcparent == NULL) 514 return; 515 516 /* Work out the size adjustment. */ 517 if (type == LAYOUT_LEFTRIGHT) 518 size = lc->sx; 519 else 520 size = lc->sy; 521 if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) 522 change = size - new_size; 523 else 524 change = new_size - size; 525 526 /* Resize the pane. */ 527 layout_resize_pane(wp, type, change); 528 } 529 530 /* Resize a single pane within the layout. */ 531 void 532 layout_resize_pane(struct window_pane *wp, enum layout_type type, int change) 533 { 534 struct window *w = wp->window; 535 struct layout_cell *lc, *lcparent; 536 int needed, size; 537 538 lc = wp->layout_cell; 539 540 /* Find next parent of the same type. */ 541 lcparent = lc->parent; 542 while (lcparent != NULL && lcparent->type != type) { 543 lc = lcparent; 544 lcparent = lc->parent; 545 } 546 if (lcparent == NULL) 547 return; 548 549 /* If this is the last cell, move back one. */ 550 if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) 551 lc = TAILQ_PREV(lc, layout_cells, entry); 552 553 /* Grow or shrink the cell. */ 554 needed = change; 555 while (needed != 0) { 556 if (change > 0) { 557 size = layout_resize_pane_grow(w, lc, type, needed); 558 needed -= size; 559 } else { 560 size = layout_resize_pane_shrink(w, lc, type, needed); 561 needed += size; 562 } 563 564 if (size == 0) /* no more change possible */ 565 break; 566 } 567 568 /* Fix cell offsets. */ 569 layout_fix_offsets(wp->window->layout_root); 570 layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); 571 notify_window_layout_changed(wp->window); 572 } 573 574 /* Helper function to grow pane. */ 575 static int 576 layout_resize_pane_grow(struct window *w, struct layout_cell *lc, 577 enum layout_type type, int needed) 578 { 579 struct layout_cell *lcadd, *lcremove; 580 u_int size; 581 582 /* Growing. Always add to the current cell. */ 583 lcadd = lc; 584 585 /* Look towards the tail for a suitable cell for reduction. */ 586 lcremove = TAILQ_NEXT(lc, entry); 587 while (lcremove != NULL) { 588 size = layout_resize_check(w, lcremove, type); 589 if (size > 0) 590 break; 591 lcremove = TAILQ_NEXT(lcremove, entry); 592 } 593 594 /* If none found, look towards the head. */ 595 if (lcremove == NULL) { 596 lcremove = TAILQ_PREV(lc, layout_cells, entry); 597 while (lcremove != NULL) { 598 size = layout_resize_check(w, lcremove, type); 599 if (size > 0) 600 break; 601 lcremove = TAILQ_PREV(lcremove, layout_cells, entry); 602 } 603 if (lcremove == NULL) 604 return (0); 605 } 606 607 /* Change the cells. */ 608 if (size > (u_int) needed) 609 size = needed; 610 layout_resize_adjust(w, lcadd, type, size); 611 layout_resize_adjust(w, lcremove, type, -size); 612 return (size); 613 } 614 615 /* Helper function to shrink pane. */ 616 static int 617 layout_resize_pane_shrink(struct window *w, struct layout_cell *lc, 618 enum layout_type type, int needed) 619 { 620 struct layout_cell *lcadd, *lcremove; 621 u_int size; 622 623 /* Shrinking. Find cell to remove from by walking towards head. */ 624 lcremove = lc; 625 do { 626 size = layout_resize_check(w, lcremove, type); 627 if (size != 0) 628 break; 629 lcremove = TAILQ_PREV(lcremove, layout_cells, entry); 630 } while (lcremove != NULL); 631 if (lcremove == NULL) 632 return (0); 633 634 /* And add onto the next cell (from the original cell). */ 635 lcadd = TAILQ_NEXT(lc, entry); 636 if (lcadd == NULL) 637 return (0); 638 639 /* Change the cells. */ 640 if (size > (u_int) -needed) 641 size = -needed; 642 layout_resize_adjust(w, lcadd, type, size); 643 layout_resize_adjust(w, lcremove, type, -size); 644 return (size); 645 } 646 647 /* Assign window pane to newly split cell. */ 648 void 649 layout_assign_pane(struct layout_cell *lc, struct window_pane *wp) 650 { 651 layout_make_leaf(lc, wp); 652 layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); 653 } 654 655 /* 656 * Split a pane into two. size is a hint, or -1 for default half/half 657 * split. This must be followed by layout_assign_pane before much else happens! 658 **/ 659 struct layout_cell * 660 layout_split_pane(struct window_pane *wp, enum layout_type type, int size, 661 int insert_before) 662 { 663 struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; 664 u_int sx, sy, xoff, yoff, size1, size2; 665 666 lc = wp->layout_cell; 667 668 /* Copy the old cell size. */ 669 sx = lc->sx; 670 sy = lc->sy; 671 xoff = lc->xoff; 672 yoff = lc->yoff; 673 674 /* Check there is enough space for the two new panes. */ 675 switch (type) { 676 case LAYOUT_LEFTRIGHT: 677 if (sx < PANE_MINIMUM * 2 + 1) 678 return (NULL); 679 break; 680 case LAYOUT_TOPBOTTOM: 681 if (sy < PANE_MINIMUM * 2 + 1) 682 return (NULL); 683 break; 684 default: 685 fatalx("bad layout type"); 686 } 687 688 if (lc->parent != NULL && lc->parent->type == type) { 689 /* 690 * If the parent exists and is of the same type as the split, 691 * create a new cell and insert it after this one. 692 */ 693 694 /* Create the new child cell. */ 695 lcparent = lc->parent; 696 lcnew = layout_create_cell(lcparent); 697 if (insert_before) 698 TAILQ_INSERT_BEFORE(lc, lcnew, entry); 699 else 700 TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry); 701 } else { 702 /* 703 * Otherwise create a new parent and insert it. 704 */ 705 706 /* Create and insert the replacement parent. */ 707 lcparent = layout_create_cell(lc->parent); 708 layout_make_node(lcparent, type); 709 layout_set_size(lcparent, sx, sy, xoff, yoff); 710 if (lc->parent == NULL) 711 wp->window->layout_root = lcparent; 712 else 713 TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry); 714 715 /* Insert the old cell. */ 716 lc->parent = lcparent; 717 TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry); 718 719 /* Create the new child cell. */ 720 lcnew = layout_create_cell(lcparent); 721 if (insert_before) 722 TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry); 723 else 724 TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry); 725 } 726 if (insert_before) { 727 lc1 = lcnew; 728 lc2 = lc; 729 } else { 730 lc1 = lc; 731 lc2 = lcnew; 732 } 733 734 /* Set new cell sizes. size is the target size or -1 for middle split, 735 * size1 is the size of the top/left and size2 the bottom/right. 736 */ 737 switch (type) { 738 case LAYOUT_LEFTRIGHT: 739 if (size < 0) 740 size2 = ((sx + 1) / 2) - 1; 741 else if (insert_before) 742 size2 = sx - size - 1; 743 else 744 size2 = size; 745 if (size2 < PANE_MINIMUM) 746 size2 = PANE_MINIMUM; 747 else if (size2 > sx - 2) 748 size2 = sx - 2; 749 size1 = sx - 1 - size2; 750 layout_set_size(lc1, size1, sy, xoff, yoff); 751 layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff); 752 break; 753 case LAYOUT_TOPBOTTOM: 754 if (size < 0) 755 size2 = ((sy + 1) / 2) - 1; 756 else if (insert_before) 757 size2 = sy - size - 1; 758 else 759 size2 = size; 760 if (size2 < PANE_MINIMUM) 761 size2 = PANE_MINIMUM; 762 else if (size2 > sy - 2) 763 size2 = sy - 2; 764 size1 = sy - 1 - size2; 765 layout_set_size(lc1, sx, size1, xoff, yoff); 766 layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1); 767 break; 768 default: 769 fatalx("bad layout type"); 770 } 771 772 /* Assign the panes. */ 773 layout_make_leaf(lc, wp); 774 775 return (lcnew); 776 } 777 778 /* Destroy the cell associated with a pane. */ 779 void 780 layout_close_pane(struct window_pane *wp) 781 { 782 struct window *w = wp->window; 783 784 /* Remove the cell. */ 785 layout_destroy_cell(w, wp->layout_cell, &w->layout_root); 786 787 /* Fix pane offsets and sizes. */ 788 if (w->layout_root != NULL) { 789 layout_fix_offsets(w->layout_root); 790 layout_fix_panes(w, w->sx, w->sy); 791 } 792 notify_window_layout_changed(w); 793 } 794