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

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