1 /* $OpenBSD: layout.c,v 1.14 2012/07/10 11:53:01 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 #include <string.h> 23 24 #include "tmux.h" 25 26 /* 27 * The window layout is a tree of cells each of which can be one of: a 28 * left-right container for a list of cells, a top-bottom container for a list 29 * of cells, or a container for a window pane. 30 * 31 * Each window has a pointer to the root of its layout tree (containing its 32 * panes), every pane has a pointer back to the cell containing it, and each 33 * cell a pointer to its parent cell. 34 */ 35 36 int layout_resize_pane_grow(struct layout_cell *, enum layout_type, int); 37 int layout_resize_pane_shrink(struct layout_cell *, enum layout_type, int); 38 39 struct layout_cell * 40 layout_create_cell(struct layout_cell *lcparent) 41 { 42 struct layout_cell *lc; 43 44 lc = xmalloc(sizeof *lc); 45 lc->type = LAYOUT_WINDOWPANE; 46 lc->parent = lcparent; 47 48 TAILQ_INIT(&lc->cells); 49 50 lc->sx = UINT_MAX; 51 lc->sy = UINT_MAX; 52 53 lc->xoff = UINT_MAX; 54 lc->yoff = UINT_MAX; 55 56 lc->wp = 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) 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, TAILQ_FIRST(&w->panes)); 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 single pane within the layout. */ 448 void 449 layout_resize_pane(struct window_pane *wp, enum layout_type type, int change) 450 { 451 struct layout_cell *lc, *lcparent; 452 int needed, 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 /* If this is the last cell, move back one. */ 466 if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) 467 lc = TAILQ_PREV(lc, layout_cells, entry); 468 469 /* Grow or shrink the cell. */ 470 needed = change; 471 while (needed != 0) { 472 if (change > 0) { 473 size = layout_resize_pane_grow(lc, type, needed); 474 needed -= size; 475 } else { 476 size = layout_resize_pane_shrink(lc, type, needed); 477 needed += size; 478 } 479 480 if (size == 0) /* no more change possible */ 481 break; 482 } 483 484 /* Fix cell offsets. */ 485 layout_fix_offsets(wp->window->layout_root); 486 layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); 487 notify_window_layout_changed(wp->window); 488 } 489 490 void 491 layout_resize_pane_mouse(struct client *c, struct mouse_event *mouse) 492 { 493 struct window *w; 494 struct window_pane *wp; 495 int pane_border; 496 497 w = c->session->curw->window; 498 499 pane_border = 0; 500 if ((c->last_mouse.b & MOUSE_BUTTON) != MOUSE_UP && 501 (c->last_mouse.b & MOUSE_RESIZE_PANE)) { 502 TAILQ_FOREACH(wp, &w->panes, entry) { 503 if (wp->xoff + wp->sx == c->last_mouse.x && 504 wp->yoff <= 1 + c->last_mouse.y && 505 wp->yoff + wp->sy >= c->last_mouse.y) { 506 layout_resize_pane(wp, LAYOUT_LEFTRIGHT, 507 mouse->x - c->last_mouse.x); 508 pane_border = 1; 509 } 510 if (wp->yoff + wp->sy == c->last_mouse.y && 511 wp->xoff <= 1 + c->last_mouse.x && 512 wp->xoff + wp->sx >= c->last_mouse.x) { 513 layout_resize_pane(wp, LAYOUT_TOPBOTTOM, 514 mouse->y - c->last_mouse.y); 515 pane_border = 1; 516 } 517 } 518 if (pane_border) 519 server_redraw_window(w); 520 } else if (mouse->b != MOUSE_UP && 521 mouse->b == (mouse->b & MOUSE_BUTTON)) { 522 TAILQ_FOREACH(wp, &w->panes, entry) { 523 if ((wp->xoff + wp->sx == mouse->x && 524 wp->yoff <= 1 + mouse->y && 525 wp->yoff + wp->sy >= mouse->y) || 526 (wp->yoff + wp->sy == mouse->y && 527 wp->xoff <= 1 + mouse->x && 528 wp->xoff + wp->sx >= mouse->x)) { 529 pane_border = 1; 530 } 531 } 532 } 533 if (pane_border) 534 mouse->b |= MOUSE_RESIZE_PANE; 535 } 536 537 int 538 layout_resize_pane_grow( 539 struct layout_cell *lc, enum layout_type type, int needed) 540 { 541 struct layout_cell *lcadd, *lcremove; 542 u_int size; 543 544 /* Growing. Always add to the current cell. */ 545 lcadd = lc; 546 547 /* Look towards the tail for a suitable cell for reduction. */ 548 lcremove = TAILQ_NEXT(lc, entry); 549 while (lcremove != NULL) { 550 size = layout_resize_check(lcremove, type); 551 if (size > 0) 552 break; 553 lcremove = TAILQ_NEXT(lcremove, entry); 554 } 555 556 /* If none found, look towards the head. */ 557 if (lcremove == NULL) { 558 lcremove = TAILQ_PREV(lc, layout_cells, entry); 559 while (lcremove != NULL) { 560 size = layout_resize_check(lcremove, type); 561 if (size > 0) 562 break; 563 lcremove = TAILQ_PREV(lcremove, layout_cells, entry); 564 } 565 if (lcremove == NULL) 566 return (0); 567 } 568 569 /* Change the cells. */ 570 if (size > (u_int) needed) 571 size = needed; 572 layout_resize_adjust(lcadd, type, size); 573 layout_resize_adjust(lcremove, type, -size); 574 return (size); 575 } 576 577 int 578 layout_resize_pane_shrink( 579 struct layout_cell *lc, enum layout_type type, int needed) 580 { 581 struct layout_cell *lcadd, *lcremove; 582 u_int size; 583 584 /* Shrinking. Find cell to remove from by walking towards head. */ 585 lcremove = lc; 586 do { 587 size = layout_resize_check(lcremove, type); 588 if (size != 0) 589 break; 590 lcremove = TAILQ_PREV(lcremove, layout_cells, entry); 591 } while (lcremove != NULL); 592 if (lcremove == NULL) 593 return (0); 594 595 /* And add onto the next cell (from the original cell). */ 596 lcadd = TAILQ_NEXT(lc, entry); 597 if (lcadd == NULL) 598 return (0); 599 600 /* Change the cells. */ 601 if (size > (u_int) -needed) 602 size = -needed; 603 layout_resize_adjust(lcadd, type, size); 604 layout_resize_adjust(lcremove, type, -size); 605 return (size); 606 } 607 608 /* Assign window pane to newly split cell. */ 609 void 610 layout_assign_pane(struct layout_cell *lc, struct window_pane *wp) 611 { 612 layout_make_leaf(lc, wp); 613 layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); 614 } 615 616 /* 617 * Split a pane into two. size is a hint, or -1 for default half/half 618 * split. This must be followed by layout_assign_pane before much else happens! 619 **/ 620 struct layout_cell * 621 layout_split_pane( 622 struct window_pane *wp, enum layout_type type, int size, int insert_before) 623 { 624 struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; 625 u_int sx, sy, xoff, yoff, size1, size2; 626 627 lc = wp->layout_cell; 628 629 /* Copy the old cell size. */ 630 sx = lc->sx; 631 sy = lc->sy; 632 xoff = lc->xoff; 633 yoff = lc->yoff; 634 635 /* Check there is enough space for the two new panes. */ 636 switch (type) { 637 case LAYOUT_LEFTRIGHT: 638 if (sx < PANE_MINIMUM * 2 + 1) 639 return (NULL); 640 break; 641 case LAYOUT_TOPBOTTOM: 642 if (sy < PANE_MINIMUM * 2 + 1) 643 return (NULL); 644 break; 645 default: 646 fatalx("bad layout type"); 647 } 648 649 if (lc->parent != NULL && lc->parent->type == type) { 650 /* 651 * If the parent exists and is of the same type as the split, 652 * create a new cell and insert it after this one. 653 */ 654 655 /* Create the new child cell. */ 656 lcparent = lc->parent; 657 lcnew = layout_create_cell(lcparent); 658 if (insert_before) 659 TAILQ_INSERT_BEFORE(lc, lcnew, entry); 660 else 661 TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry); 662 } else { 663 /* 664 * Otherwise create a new parent and insert it. 665 */ 666 667 /* Create and insert the replacement parent. */ 668 lcparent = layout_create_cell(lc->parent); 669 layout_make_node(lcparent, type); 670 layout_set_size(lcparent, sx, sy, xoff, yoff); 671 if (lc->parent == NULL) 672 wp->window->layout_root = lcparent; 673 else 674 TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry); 675 676 /* Insert the old cell. */ 677 lc->parent = lcparent; 678 TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry); 679 680 /* Create the new child cell. */ 681 lcnew = layout_create_cell(lcparent); 682 if (insert_before) 683 TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry); 684 else 685 TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry); 686 } 687 if (insert_before) { 688 lc1 = lcnew; 689 lc2 = lc; 690 } else { 691 lc1 = lc; 692 lc2 = lcnew; 693 } 694 695 /* Set new cell sizes. size is the target size or -1 for middle split, 696 * size1 is the size of the top/left and size2 the bottom/right. 697 */ 698 switch (type) { 699 case LAYOUT_LEFTRIGHT: 700 if (size < 0) 701 size2 = ((sx + 1) / 2) - 1; 702 else 703 size2 = size; 704 if (size2 < PANE_MINIMUM) 705 size2 = PANE_MINIMUM; 706 else if (size2 > sx - 2) 707 size2 = sx - 2; 708 size1 = sx - 1 - size2; 709 layout_set_size(lc1, size1, sy, xoff, yoff); 710 layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff); 711 break; 712 case LAYOUT_TOPBOTTOM: 713 if (size < 0) 714 size2 = ((sy + 1) / 2) - 1; 715 else 716 size2 = size; 717 if (size2 < PANE_MINIMUM) 718 size2 = PANE_MINIMUM; 719 else if (size2 > sy - 2) 720 size2 = sy - 2; 721 size1 = sy - 1 - size2; 722 layout_set_size(lc1, sx, size1, xoff, yoff); 723 layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1); 724 break; 725 default: 726 fatalx("bad layout type"); 727 } 728 729 /* Assign the panes. */ 730 layout_make_leaf(lc, wp); 731 732 return (lcnew); 733 } 734 735 /* Destroy the cell associated with a pane. */ 736 void 737 layout_close_pane(struct window_pane *wp) 738 { 739 /* Remove the cell. */ 740 layout_destroy_cell(wp->layout_cell, &wp->window->layout_root); 741 742 /* Fix pane offsets and sizes. */ 743 if (wp->window->layout_root != NULL) { 744 layout_fix_offsets(wp->window->layout_root); 745 layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); 746 } 747 notify_window_layout_changed(wp->window); 748 } 749 750 /* Add layout to list. */ 751 void 752 layout_list_add(struct window *w) 753 { 754 struct last_layout *ll, *ll_last; 755 char *layout; 756 u_int limit; 757 758 layout = layout_dump(w); 759 760 ll_last = w->layout_list_last; 761 if (ll_last != NULL && strcmp(ll_last->layout, layout) == 0) { 762 free(layout); 763 return; 764 } 765 766 ll = xmalloc(sizeof *ll); 767 ll->layout = layout; 768 if (ll_last == NULL) 769 TAILQ_INSERT_TAIL(&w->layout_list, ll, entry); 770 else 771 TAILQ_INSERT_AFTER(&w->layout_list, ll_last, ll, entry); 772 w->layout_list_size++; 773 w->layout_list_last = ll; 774 775 limit = options_get_number(&w->options, "layout-history-limit"); 776 while (w->layout_list_size > limit) { 777 ll = TAILQ_LAST(&w->layout_list, last_layouts); 778 if (ll == w->layout_list_last) 779 ll = TAILQ_FIRST(&w->layout_list); 780 781 TAILQ_REMOVE(&w->layout_list, ll, entry); 782 w->layout_list_size--; 783 784 free(ll->layout); 785 free(ll); 786 } 787 } 788 789 /* Apply next layout from list. */ 790 const char * 791 layout_list_redo(struct window *w) 792 { 793 struct last_layout *ll, *ll_last; 794 795 ll_last = w->layout_list_last; 796 if (ll_last == NULL) 797 return (NULL); 798 ll = TAILQ_NEXT(ll_last, entry); 799 if (ll == NULL) 800 return (NULL); 801 w->layout_list_last = ll; 802 return (ll->layout); 803 } 804 805 /* Apply previous layout from list. */ 806 const char * 807 layout_list_undo(struct window *w) 808 { 809 struct last_layout *ll, *ll_last; 810 811 ll_last = w->layout_list_last; 812 if (ll_last == NULL) 813 return (NULL); 814 ll = TAILQ_PREV(ll_last, last_layouts, entry); 815 if (ll == NULL) 816 return (NULL); 817 w->layout_list_last = ll; 818 return (ll->layout); 819 } 820