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