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

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