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