[BACK]Return to layout.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/layout.c, Revision 1.35

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