Annotation of embedaddon/tmux/layout.c, revision 1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>