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

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