[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.41

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