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

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