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

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