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

1.18    ! nicm        1: /* $OpenBSD: layout.c,v 1.17 2013/03/22 10:37:39 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.14      nicm       80:        free(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.18    ! nicm      377: layout_init(struct window *w, struct window_pane *wp)
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);
1.18    ! nicm      383:        layout_make_leaf(lc, wp);
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.17      nicm      446: /* Resize a pane to an absolute size. */
                    447: void
                    448: layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
                    449:     u_int new_size)
                    450: {
                    451:        struct layout_cell     *lc, *lcparent;
                    452:        int                     change, size;
                    453:
                    454:        lc = wp->layout_cell;
                    455:
                    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:        /* Work out the size adjustment. */
                    466:        if (type == LAYOUT_LEFTRIGHT)
                    467:                size = lc->sx;
                    468:        else
                    469:                size = lc->sy;
                    470:        if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
                    471:                change = size - new_size;
                    472:        else
                    473:                change = new_size - size;
                    474:
                    475:        /* Resize the pane. */
                    476:        layout_resize_pane(wp, type, change);
                    477: }
                    478:
1.3       nicm      479: /* Resize a single pane within the layout. */
1.1       nicm      480: void
1.3       nicm      481: layout_resize_pane(struct window_pane *wp, enum layout_type type, int change)
1.1       nicm      482: {
1.3       nicm      483:        struct layout_cell     *lc, *lcparent;
                    484:        int                     needed, size;
1.1       nicm      485:
1.3       nicm      486:        lc = wp->layout_cell;
1.1       nicm      487:
1.3       nicm      488:        /* Find next parent of the same type. */
                    489:        lcparent = lc->parent;
                    490:        while (lcparent != NULL && lcparent->type != type) {
                    491:                lc = lcparent;
                    492:                lcparent = lc->parent;
                    493:        }
                    494:        if (lcparent == NULL)
                    495:                return;
                    496:
                    497:        /* If this is the last cell, move back one. */
                    498:        if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
                    499:                lc = TAILQ_PREV(lc, layout_cells, entry);
                    500:
                    501:        /* Grow or shrink the cell. */
                    502:        needed = change;
                    503:        while (needed != 0) {
                    504:                if (change > 0) {
                    505:                        size = layout_resize_pane_grow(lc, type, needed);
                    506:                        needed -= size;
                    507:                } else {
                    508:                        size = layout_resize_pane_shrink(lc, type, needed);
                    509:                        needed += size;
                    510:                }
1.1       nicm      511:
1.3       nicm      512:                if (size == 0)  /* no more change possible */
                    513:                        break;
                    514:        }
1.4       nicm      515:
1.3       nicm      516:        /* Fix cell offsets. */
                    517:        layout_fix_offsets(wp->window->layout_root);
                    518:        layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
1.10      nicm      519:        notify_window_layout_changed(wp->window);
1.7       nicm      520: }
                    521:
1.17      nicm      522: /* Resize pane based on mouse events. */
1.7       nicm      523: void
1.15      nicm      524: layout_resize_pane_mouse(struct client *c)
1.7       nicm      525: {
                    526:        struct window           *w;
                    527:        struct window_pane      *wp;
1.15      nicm      528:        struct mouse_event      *m = &c->tty.mouse;
1.7       nicm      529:        int                      pane_border;
                    530:
                    531:        w = c->session->curw->window;
                    532:
                    533:        pane_border = 0;
1.15      nicm      534:        if (m->event & MOUSE_EVENT_DRAG && m->flags & MOUSE_RESIZE_PANE) {
1.7       nicm      535:                TAILQ_FOREACH(wp, &w->panes, entry) {
1.15      nicm      536:                        if (wp->xoff + wp->sx == m->lx &&
                    537:                            wp->yoff <= 1 + m->ly &&
                    538:                            wp->yoff + wp->sy >= m->ly) {
1.7       nicm      539:                                layout_resize_pane(wp, LAYOUT_LEFTRIGHT,
1.15      nicm      540:                                    m->x - m->lx);
1.7       nicm      541:                                pane_border = 1;
                    542:                        }
1.15      nicm      543:                        if (wp->yoff + wp->sy == m->ly &&
                    544:                            wp->xoff <= 1 + m->lx &&
                    545:                            wp->xoff + wp->sx >= m->lx) {
1.7       nicm      546:                                layout_resize_pane(wp, LAYOUT_TOPBOTTOM,
1.15      nicm      547:                                    m->y - m->ly);
1.7       nicm      548:                                pane_border = 1;
                    549:                        }
                    550:                }
                    551:                if (pane_border)
                    552:                        server_redraw_window(w);
1.15      nicm      553:        } else if (~m->event & MOUSE_EVENT_UP) {
1.7       nicm      554:                TAILQ_FOREACH(wp, &w->panes, entry) {
1.15      nicm      555:                        if ((wp->xoff + wp->sx == m->x &&
                    556:                            wp->yoff <= 1 + m->y &&
                    557:                            wp->yoff + wp->sy >= m->y) ||
                    558:                            (wp->yoff + wp->sy == m->y &&
                    559:                            wp->xoff <= 1 + m->x &&
                    560:                            wp->xoff + wp->sx >= m->x)) {
1.7       nicm      561:                                pane_border = 1;
                    562:                        }
                    563:                }
                    564:        }
                    565:        if (pane_border)
1.15      nicm      566:                m->flags |= MOUSE_RESIZE_PANE;
                    567:        else
                    568:                m->flags &= ~MOUSE_RESIZE_PANE;
1.3       nicm      569: }
1.1       nicm      570:
1.17      nicm      571: /* Helper function to grow pane. */
1.3       nicm      572: int
                    573: layout_resize_pane_grow(
                    574:     struct layout_cell *lc, enum layout_type type, int needed)
                    575: {
                    576:        struct layout_cell      *lcadd, *lcremove;
                    577:        u_int                    size;
                    578:
                    579:        /* Growing. Always add to the current cell. */
                    580:        lcadd = lc;
1.4       nicm      581:
1.3       nicm      582:        /* Look towards the tail for a suitable cell for reduction. */
                    583:        lcremove = TAILQ_NEXT(lc, entry);
                    584:        while (lcremove != NULL) {
                    585:                size = layout_resize_check(lcremove, type);
                    586:                if (size > 0)
                    587:                        break;
1.4       nicm      588:                lcremove = TAILQ_NEXT(lcremove, entry);
1.1       nicm      589:        }
                    590:
1.3       nicm      591:        /* If none found, look towards the head. */
                    592:        if (lcremove == NULL) {
                    593:                lcremove = TAILQ_PREV(lc, layout_cells, entry);
                    594:                while (lcremove != NULL) {
                    595:                        size = layout_resize_check(lcremove, type);
                    596:                        if (size > 0)
                    597:                                break;
                    598:                        lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
1.1       nicm      599:                }
1.3       nicm      600:                if (lcremove == NULL)
                    601:                        return (0);
                    602:        }
1.1       nicm      603:
1.3       nicm      604:        /* Change the cells. */
                    605:        if (size > (u_int) needed)
                    606:                size = needed;
                    607:        layout_resize_adjust(lcadd, type, size);
                    608:        layout_resize_adjust(lcremove, type, -size);
                    609:        return (size);
                    610: }
1.1       nicm      611:
1.17      nicm      612: /* Helper function to shrink pane. */
1.3       nicm      613: int
                    614: layout_resize_pane_shrink(
                    615:     struct layout_cell *lc, enum layout_type type, int needed)
                    616: {
                    617:        struct layout_cell      *lcadd, *lcremove;
                    618:        u_int                    size;
1.1       nicm      619:
1.3       nicm      620:        /* Shrinking. Find cell to remove from by walking towards head. */
                    621:        lcremove = lc;
                    622:        do {
                    623:                size = layout_resize_check(lcremove, type);
                    624:                if (size != 0)
1.1       nicm      625:                        break;
1.3       nicm      626:                lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
                    627:        } while (lcremove != NULL);
                    628:        if (lcremove == NULL)
                    629:                return (0);
                    630:
                    631:        /* And add onto the next cell (from the original cell). */
                    632:        lcadd = TAILQ_NEXT(lc, entry);
                    633:        if (lcadd == NULL)
                    634:                return (0);
                    635:
                    636:        /* Change the cells. */
                    637:        if (size > (u_int) -needed)
                    638:                size = -needed;
                    639:        layout_resize_adjust(lcadd, type, size);
                    640:        layout_resize_adjust(lcremove, type, -size);
                    641:        return (size);
1.1       nicm      642: }
                    643:
1.5       nicm      644: /* Assign window pane to newly split cell. */
                    645: void
                    646: layout_assign_pane(struct layout_cell *lc, struct window_pane *wp)
                    647: {
                    648:        layout_make_leaf(lc, wp);
                    649:        layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
                    650: }
                    651:
                    652: /*
                    653:  * Split a pane into two. size is a hint, or -1 for default half/half
                    654:  * split. This must be followed by layout_assign_pane before much else happens!
                    655:  **/
                    656: struct layout_cell *
1.9       nicm      657: layout_split_pane(
                    658:     struct window_pane *wp, enum layout_type type, int size, int insert_before)
1.1       nicm      659: {
1.9       nicm      660:        struct layout_cell     *lc, *lcparent, *lcnew, *lc1, *lc2;
1.3       nicm      661:        u_int                   sx, sy, xoff, yoff, size1, size2;
1.1       nicm      662:
1.3       nicm      663:        lc = wp->layout_cell;
1.1       nicm      664:
1.3       nicm      665:        /* Copy the old cell size. */
                    666:        sx = lc->sx;
                    667:        sy = lc->sy;
                    668:        xoff = lc->xoff;
                    669:        yoff = lc->yoff;
                    670:
                    671:        /* Check there is enough space for the two new panes. */
                    672:        switch (type) {
                    673:        case LAYOUT_LEFTRIGHT:
                    674:                if (sx < PANE_MINIMUM * 2 + 1)
1.5       nicm      675:                        return (NULL);
1.3       nicm      676:                break;
                    677:        case LAYOUT_TOPBOTTOM:
                    678:                if (sy < PANE_MINIMUM * 2 + 1)
1.5       nicm      679:                        return (NULL);
1.3       nicm      680:                break;
                    681:        default:
                    682:                fatalx("bad layout type");
                    683:        }
1.4       nicm      684:
1.3       nicm      685:        if (lc->parent != NULL && lc->parent->type == type) {
                    686:                /*
                    687:                 * If the parent exists and is of the same type as the split,
                    688:                 * create a new cell and insert it after this one.
                    689:                 */
                    690:
                    691:                /* Create the new child cell. */
1.9       nicm      692:                lcparent = lc->parent;
                    693:                lcnew = layout_create_cell(lcparent);
                    694:                if (insert_before)
                    695:                        TAILQ_INSERT_BEFORE(lc, lcnew, entry);
                    696:                else
                    697:                        TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
1.3       nicm      698:        } else {
                    699:                /*
                    700:                 * Otherwise create a new parent and insert it.
                    701:                 */
1.4       nicm      702:
1.3       nicm      703:                /* Create and insert the replacement parent. */
                    704:                lcparent = layout_create_cell(lc->parent);
                    705:                layout_make_node(lcparent, type);
                    706:                layout_set_size(lcparent, sx, sy, xoff, yoff);
                    707:                if (lc->parent == NULL)
                    708:                        wp->window->layout_root = lcparent;
                    709:                else
                    710:                        TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
1.4       nicm      711:
1.3       nicm      712:                /* Insert the old cell. */
                    713:                lc->parent = lcparent;
                    714:                TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
1.4       nicm      715:
1.3       nicm      716:                /* Create the new child cell. */
                    717:                lcnew = layout_create_cell(lcparent);
1.9       nicm      718:                if (insert_before)
                    719:                        TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry);
                    720:                else
                    721:                        TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
                    722:        }
                    723:        if (insert_before) {
                    724:                lc1 = lcnew;
                    725:                lc2 = lc;
                    726:        } else {
                    727:                lc1 = lc;
                    728:                lc2 = lcnew;
1.3       nicm      729:        }
                    730:
                    731:        /* Set new cell sizes.  size is the target size or -1 for middle split,
                    732:         * size1 is the size of the top/left and size2 the bottom/right.
                    733:         */
                    734:        switch (type) {
                    735:        case LAYOUT_LEFTRIGHT:
                    736:                if (size < 0)
                    737:                        size2 = ((sx + 1) / 2) - 1;
1.4       nicm      738:                else
1.3       nicm      739:                        size2 = size;
                    740:                if (size2 < PANE_MINIMUM)
                    741:                        size2 = PANE_MINIMUM;
                    742:                else if (size2 > sx - 2)
                    743:                        size2 = sx - 2;
                    744:                size1 = sx - 1 - size2;
1.9       nicm      745:                layout_set_size(lc1, size1, sy, xoff, yoff);
                    746:                layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff);
1.3       nicm      747:                break;
                    748:        case LAYOUT_TOPBOTTOM:
                    749:                if (size < 0)
                    750:                        size2 = ((sy + 1) / 2) - 1;
                    751:                else
                    752:                        size2 = size;
                    753:                if (size2 < PANE_MINIMUM)
                    754:                        size2 = PANE_MINIMUM;
                    755:                else if (size2 > sy - 2)
                    756:                        size2 = sy - 2;
                    757:                size1 = sy - 1 - size2;
1.9       nicm      758:                layout_set_size(lc1, sx, size1, xoff, yoff);
                    759:                layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1);
1.3       nicm      760:                break;
                    761:        default:
                    762:                fatalx("bad layout type");
                    763:        }
                    764:
                    765:        /* Assign the panes. */
                    766:        layout_make_leaf(lc, wp);
1.1       nicm      767:
1.5       nicm      768:        return (lcnew);
1.3       nicm      769: }
1.1       nicm      770:
1.6       nicm      771: /* Destroy the cell associated with a pane. */
1.3       nicm      772: void
                    773: layout_close_pane(struct window_pane *wp)
                    774: {
1.6       nicm      775:        /* Remove the cell. */
                    776:        layout_destroy_cell(wp->layout_cell, &wp->window->layout_root);
1.1       nicm      777:
1.6       nicm      778:        /* Fix pane offsets and sizes. */
                    779:        if (wp->window->layout_root != NULL) {
                    780:                layout_fix_offsets(wp->window->layout_root);
                    781:                layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
1.3       nicm      782:        }
1.10      nicm      783:        notify_window_layout_changed(wp->window);
1.1       nicm      784: }