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