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

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