[BACK]Return to format-draw.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/format-draw.c, Revision 1.1

1.1     ! nicm        1: /* $OpenBSD$ */
        !             2:
        !             3: /*
        !             4:  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
        !             5:  *
        !             6:  * Permission to use, copy, modify, and distribute this software for any
        !             7:  * purpose with or without fee is hereby granted, provided that the above
        !             8:  * copyright notice and this permission notice appear in all copies.
        !             9:  *
        !            10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            14:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
        !            15:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
        !            16:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            17:  */
        !            18:
        !            19: #include <sys/types.h>
        !            20:
        !            21: #include <stdlib.h>
        !            22: #include <string.h>
        !            23:
        !            24: #include "tmux.h"
        !            25:
        !            26: /* Format range. */
        !            27: struct format_range {
        !            28:        u_int                            index;
        !            29:        struct screen                   *s;
        !            30:
        !            31:        u_int                            start;
        !            32:        u_int                            end;
        !            33:
        !            34:        enum style_range_type            type;
        !            35:        u_int                            argument;
        !            36:
        !            37:        TAILQ_ENTRY(format_range)        entry;
        !            38: };
        !            39: TAILQ_HEAD(format_ranges, format_range);
        !            40:
        !            41: /* Does this range match this style? */
        !            42: static int
        !            43: format_is_type(struct format_range *fr, struct style *sy)
        !            44: {
        !            45:        if (fr->type != sy->range_type)
        !            46:                return (0);
        !            47:        if (fr->type == STYLE_RANGE_WINDOW &&
        !            48:            fr->argument != sy->range_argument)
        !            49:                return (0);
        !            50:        return (1);
        !            51: }
        !            52:
        !            53: /* Free a range. */
        !            54: static void
        !            55: format_free_range(struct format_ranges *frs, struct format_range *fr)
        !            56: {
        !            57:        TAILQ_REMOVE(frs, fr, entry);
        !            58:        free(fr);
        !            59: }
        !            60:
        !            61: /* Fix range positions. */
        !            62: static void
        !            63: format_update_ranges(struct format_ranges *frs, struct screen *s, u_int offset,
        !            64:     u_int start, u_int width)
        !            65: {
        !            66:        struct format_range     *fr, *fr1;
        !            67:
        !            68:        if (frs == NULL)
        !            69:                return;
        !            70:
        !            71:        TAILQ_FOREACH_SAFE(fr, frs, entry, fr1) {
        !            72:                if (fr->s != s)
        !            73:                        continue;
        !            74:
        !            75:                if (fr->end <= start || fr->start >= start + width) {
        !            76:                        format_free_range(frs, fr);
        !            77:                        continue;
        !            78:                }
        !            79:
        !            80:                if (fr->start < start)
        !            81:                        fr->start = start;
        !            82:                if (fr->end > start + width)
        !            83:                        fr->end = start + width;
        !            84:                if (fr->start == fr->end) {
        !            85:                        format_free_range(frs, fr);
        !            86:                        continue;
        !            87:                }
        !            88:
        !            89:                fr->start += offset;
        !            90:                fr->end += offset;
        !            91:        }
        !            92: }
        !            93:
        !            94: /* Draw a part of the format. */
        !            95: static void
        !            96: format_draw_put(struct screen_write_ctx *octx, u_int ocx, u_int ocy,
        !            97:     struct screen *s, struct format_ranges *frs, u_int offset, u_int start,
        !            98:     u_int width)
        !            99: {
        !           100:        /*
        !           101:         * The offset is how far from the cursor on the target screen; start
        !           102:         * and width how much to copy from the source screen.
        !           103:         */
        !           104:        screen_write_cursormove(octx, ocx + offset, ocy, 0);
        !           105:        screen_write_fast_copy(octx, s, start, 0, width, 1);
        !           106:        format_update_ranges(frs, s, offset, start, width);
        !           107: }
        !           108:
        !           109: /* Draw list part of format. */
        !           110: static void
        !           111: format_draw_put_list(struct screen_write_ctx *octx,
        !           112:     u_int ocx, u_int ocy, u_int offset, u_int width, struct screen *list,
        !           113:     struct screen *list_left, struct screen *list_right, int focus_start,
        !           114:     int focus_end, struct format_ranges *frs)
        !           115: {
        !           116:        u_int   start, focus_centre;
        !           117:
        !           118:        /* If there is enough space for the list, draw it entirely. */
        !           119:        if (width >= list->cx) {
        !           120:                format_draw_put(octx, ocx, ocy, list, frs, offset, 0, width);
        !           121:                return;
        !           122:        }
        !           123:
        !           124:        /* The list needs to be trimmed. Try to keep the focus visible. */
        !           125:        focus_centre = focus_start + (focus_end - focus_start) / 2;
        !           126:        if (focus_centre < width / 2)
        !           127:                start = 0;
        !           128:        else
        !           129:                start = focus_centre - width / 2;
        !           130:        if (start + width > list->cx)
        !           131:                start = list->cx - width;
        !           132:
        !           133:        /* Draw <> markers at either side if needed. */
        !           134:        if (start != 0 && width > list_left->cx) {
        !           135:                screen_write_cursormove(octx, ocx + offset, ocy, 0);
        !           136:                screen_write_fast_copy(octx, list_left, 0, 0, list_left->cx, 1);
        !           137:                offset += list_left->cx;
        !           138:                start += list_left->cx;
        !           139:                width -= list_left->cx;
        !           140:        }
        !           141:        if (start + width < list->cx && width > list_right->cx) {
        !           142:                screen_write_cursormove(octx, ocx + offset + width - 1, ocy, 0);
        !           143:                screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
        !           144:                    1);
        !           145:                width -= list_right->cx;
        !           146:        }
        !           147:
        !           148:        /* Draw the list screen itself. */
        !           149:        format_draw_put(octx, ocx, ocy, list, frs, offset, start, width);
        !           150: }
        !           151:
        !           152: /* Draw format with no list. */
        !           153: static void
        !           154: format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
        !           155:     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
        !           156:     struct format_ranges *frs)
        !           157: {
        !           158:        u_int   width_left, width_centre, width_right;
        !           159:
        !           160:        width_left = left->cx;
        !           161:        width_centre = centre->cx;
        !           162:        width_right = right->cx;
        !           163:
        !           164:        /*
        !           165:         * Try to keep as much of the left and right as possible at the expense
        !           166:         * of the centre.
        !           167:         */
        !           168:        while (width_left + width_centre + width_right > available) {
        !           169:                if (width_centre > 0)
        !           170:                        width_centre--;
        !           171:                else if (width_right > 0)
        !           172:                        width_right--;
        !           173:                else
        !           174:                        width_left--;
        !           175:        }
        !           176:
        !           177:        /* Write left. */
        !           178:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
        !           179:
        !           180:        /* Write right at available - width_right. */
        !           181:        format_draw_put(octx, ocx, ocy, right, frs,
        !           182:            available - width_right,
        !           183:            right->cx - width_right,
        !           184:            width_right);
        !           185:
        !           186:        /*
        !           187:         * Write centre halfway between
        !           188:         *     width_left
        !           189:         * and
        !           190:         *     available - width_right.
        !           191:         */
        !           192:        format_draw_put(octx, ocx, ocy, centre, frs,
        !           193:            width_left
        !           194:            + ((available - width_right) - width_left) / 2
        !           195:            - width_centre / 2,
        !           196:            centre->cx / 2 - width_centre / 2,
        !           197:            width_centre);
        !           198: }
        !           199:
        !           200: /* Draw format with list on the left. */
        !           201: static void
        !           202: format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
        !           203:     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
        !           204:     struct screen *list, struct screen *list_left, struct screen *list_right,
        !           205:     struct screen *after, int focus_start, int focus_end,
        !           206:     struct format_ranges *frs)
        !           207: {
        !           208:        u_int                   width_left, width_centre, width_right;
        !           209:        u_int                   width_list, width_after;
        !           210:        struct screen_write_ctx ctx;
        !           211:
        !           212:        width_left = left->cx;
        !           213:        width_centre = centre->cx;
        !           214:        width_right = right->cx;
        !           215:        width_list = list->cx;
        !           216:        width_after = after->cx;
        !           217:
        !           218:        /*
        !           219:         * Trim first the centre, then the list, then the right, then after the
        !           220:         * list, then the left.
        !           221:         */
        !           222:        while (width_left +
        !           223:            width_centre +
        !           224:            width_right +
        !           225:            width_list +
        !           226:            width_after > available) {
        !           227:                if (width_centre > 0)
        !           228:                        width_centre--;
        !           229:                else if (width_list > 0)
        !           230:                        width_list--;
        !           231:                else if (width_right > 0)
        !           232:                        width_right--;
        !           233:                else if (width_after > 0)
        !           234:                        width_after--;
        !           235:                else
        !           236:                        width_left--;
        !           237:        }
        !           238:
        !           239:        /* If there is no list left, pass off to the no list function. */
        !           240:        if (width_list == 0) {
        !           241:                screen_write_start(&ctx, NULL, left);
        !           242:                screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
        !           243:                screen_write_stop(&ctx);
        !           244:
        !           245:                format_draw_none(octx, available, ocx, ocy, left, centre,
        !           246:                    right, frs);
        !           247:                return;
        !           248:        }
        !           249:
        !           250:        /* Write left at 0. */
        !           251:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
        !           252:
        !           253:        /* Write right at available - width_right. */
        !           254:        format_draw_put(octx, ocx, ocy, right, frs,
        !           255:            available - width_right,
        !           256:            right->cx - width_right,
        !           257:            width_right);
        !           258:
        !           259:        /* Write after at width_left + width_list. */
        !           260:        format_draw_put(octx, ocx, ocy, after, frs,
        !           261:            width_left + width_list,
        !           262:            0,
        !           263:            width_after);
        !           264:
        !           265:        /*
        !           266:         * Write centre halfway between
        !           267:         *     width_left + width_list + width_after
        !           268:         * and
        !           269:         *     available - width_right.
        !           270:         */
        !           271:        format_draw_put(octx, ocx, ocy, centre, frs,
        !           272:            (width_left + width_list + width_after)
        !           273:            + ((available - width_right)
        !           274:                - (width_left + width_list + width_after)) / 2
        !           275:            - width_centre / 2,
        !           276:            centre->cx / 2 - width_centre / 2,
        !           277:            width_centre);
        !           278:
        !           279:        /*
        !           280:         * The list now goes from
        !           281:         *     width_left
        !           282:         * to
        !           283:         *     width_left + width_list.
        !           284:         * If there is no focus given, keep the left in focus.
        !           285:         */
        !           286:        if (focus_start == -1 || focus_end == -1)
        !           287:                focus_start = focus_end = 0;
        !           288:        format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
        !           289:            list_left, list_right, focus_start, focus_end, frs);
        !           290: }
        !           291:
        !           292: /* Draw format with list in the centre. */
        !           293: static void
        !           294: format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
        !           295:     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
        !           296:     struct screen *list, struct screen *list_left, struct screen *list_right,
        !           297:     struct screen *after, int focus_start, int focus_end,
        !           298:     struct format_ranges *frs)
        !           299: {
        !           300:        u_int                   width_left, width_centre, width_right;
        !           301:        u_int                   width_list, width_after, middle;
        !           302:        struct screen_write_ctx ctx;
        !           303:
        !           304:        width_left = left->cx;
        !           305:        width_centre = centre->cx;
        !           306:        width_right = right->cx;
        !           307:        width_list = list->cx;
        !           308:        width_after = after->cx;
        !           309:
        !           310:        /*
        !           311:         * Trim first the list, then after the list, then the centre, then the
        !           312:         * right, then the left.
        !           313:         */
        !           314:        while (width_left +
        !           315:            width_centre +
        !           316:            width_right +
        !           317:            width_list +
        !           318:            width_after > available) {
        !           319:                if (width_list > 0)
        !           320:                        width_list--;
        !           321:                else if (width_after > 0)
        !           322:                        width_after--;
        !           323:                else if (width_centre > 0)
        !           324:                        width_centre--;
        !           325:                else if (width_right > 0)
        !           326:                        width_right--;
        !           327:                else
        !           328:                        width_left--;
        !           329:        }
        !           330:
        !           331:        /* If there is no list left, pass off to the no list function. */
        !           332:        if (width_list == 0) {
        !           333:                screen_write_start(&ctx, NULL, centre);
        !           334:                screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
        !           335:                screen_write_stop(&ctx);
        !           336:
        !           337:                format_draw_none(octx, available, ocx, ocy, left, centre,
        !           338:                    right, frs);
        !           339:                return;
        !           340:        }
        !           341:
        !           342:        /* Write left at 0. */
        !           343:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
        !           344:
        !           345:        /* Write after at available - width_after. */
        !           346:        format_draw_put(octx, ocx, ocy, after, frs,
        !           347:            available - width_after,
        !           348:            after->cx - width_after,
        !           349:            width_after);
        !           350:
        !           351:        /* Write right at available - width_right. */
        !           352:        format_draw_put(octx, ocx, ocy, right, frs,
        !           353:            available - width_right,
        !           354:            right->cx - width_right,
        !           355:            width_right);
        !           356:
        !           357:        /*
        !           358:         * All three centre sections are offset from the middle of the
        !           359:         * available space.
        !           360:         */
        !           361:        middle = (width_left + ((available - width_right) - width_left) / 2);
        !           362:
        !           363:        /*
        !           364:         * Write centre at
        !           365:         *     middle - width_list / 2 - width_centre.
        !           366:         */
        !           367:        format_draw_put(octx, ocx, ocy, centre, frs,
        !           368:            middle - width_list / 2 - width_centre,
        !           369:            0,
        !           370:            width_centre);
        !           371:
        !           372:        /*
        !           373:         * Write after at
        !           374:         *     middle + width_list / 2 - width_centre.
        !           375:         */
        !           376:        format_draw_put(octx, ocx, ocy, after, frs,
        !           377:            middle + width_list / 2,
        !           378:            0,
        !           379:            width_after);
        !           380:
        !           381:        /*
        !           382:         * The list now goes from
        !           383:         *     middle - width_list / 2
        !           384:         * to
        !           385:         *     middle + width_list / 2
        !           386:         * If there is no focus given, keep the centre in focus.
        !           387:         */
        !           388:        if (focus_start == -1 || focus_end == -1)
        !           389:                focus_start = focus_end = list->cx / 2;
        !           390:        format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
        !           391:            width_list, list, list_left, list_right, focus_start, focus_end,
        !           392:            frs);
        !           393: }
        !           394:
        !           395: /* Draw format with list on the right. */
        !           396: static void
        !           397: format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
        !           398:     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
        !           399:     struct screen *list, struct screen *list_left, struct screen *list_right,
        !           400:     struct screen *after, int focus_start, int focus_end,
        !           401:     struct format_ranges *frs)
        !           402: {
        !           403:        u_int                   width_left, width_centre, width_right;
        !           404:        u_int                   width_list, width_after;
        !           405:        struct screen_write_ctx ctx;
        !           406:
        !           407:        width_left = left->cx;
        !           408:        width_centre = centre->cx;
        !           409:        width_right = right->cx;
        !           410:        width_list = list->cx;
        !           411:        width_after = after->cx;
        !           412:
        !           413:        /*
        !           414:         * Trim first the centre, then the list, then the right, then
        !           415:         * after the list, then the left.
        !           416:         */
        !           417:        while (width_left +
        !           418:            width_centre +
        !           419:            width_right +
        !           420:            width_list +
        !           421:            width_after > available) {
        !           422:                if (width_centre > 0)
        !           423:                        width_centre--;
        !           424:                else if (width_list > 0)
        !           425:                        width_list--;
        !           426:                else if (width_right > 0)
        !           427:                        width_right--;
        !           428:                else if (width_after > 0)
        !           429:                        width_after--;
        !           430:                else
        !           431:                        width_left--;
        !           432:        }
        !           433:
        !           434:        /* If there is no list left, pass off to the no list function. */
        !           435:        if (width_list == 0) {
        !           436:                screen_write_start(&ctx, NULL, right);
        !           437:                screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
        !           438:                screen_write_stop(&ctx);
        !           439:
        !           440:                format_draw_none(octx, available, ocx, ocy, left, centre,
        !           441:                    right, frs);
        !           442:                return;
        !           443:        }
        !           444:
        !           445:        /* Write left at 0. */
        !           446:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
        !           447:
        !           448:        /* Write after at available - width_after. */
        !           449:        format_draw_put(octx, ocx, ocy, after, frs,
        !           450:            available - width_after,
        !           451:            after->cx - width_after,
        !           452:            width_after);
        !           453:
        !           454:        /*
        !           455:         * Write right at
        !           456:         *     available - width_right - width_list - width_after.
        !           457:         */
        !           458:        format_draw_put(octx, ocx, ocy, right, frs,
        !           459:            available - width_right - width_list - width_after,
        !           460:            0,
        !           461:            width_right);
        !           462:
        !           463:        /*
        !           464:         * Write centre halfway between
        !           465:         *     width_left
        !           466:         * and
        !           467:         *     available - width_right - width_list - width_after.
        !           468:         */
        !           469:        format_draw_put(octx, ocx, ocy, centre, frs,
        !           470:            width_left
        !           471:            + ((available - width_right - width_list - width_after)
        !           472:                - width_left) / 2
        !           473:            - width_centre / 2,
        !           474:            centre->cx / 2 - width_centre / 2,
        !           475:            width_centre);
        !           476:
        !           477:        /*
        !           478:         * The list now goes from
        !           479:         *     available - width_list - width_after
        !           480:         * to
        !           481:         *     available - width_after
        !           482:         * If there is no focus given, keep the right in focus.
        !           483:         */
        !           484:        if (focus_start == -1 || focus_end == -1)
        !           485:                focus_start = focus_end = 0;
        !           486:        format_draw_put_list(octx, ocx, ocy, available - width_list -
        !           487:            width_after, width_list, list, list_left, list_right, focus_start,
        !           488:            focus_end, frs);
        !           489: }
        !           490:
        !           491: /* Draw a format to a screen. */
        !           492: void
        !           493: format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
        !           494:     u_int available, const char *expanded, struct style_ranges *srs)
        !           495: {
        !           496:        enum { LEFT,
        !           497:               CENTRE,
        !           498:               RIGHT,
        !           499:               LIST,
        !           500:               LIST_LEFT,
        !           501:               LIST_RIGHT,
        !           502:               AFTER,
        !           503:               TOTAL } current = LEFT, last = LEFT;
        !           504:        const char              *names[] = { "LEFT",
        !           505:                                             "CENTRE",
        !           506:                                             "RIGHT",
        !           507:                                             "LIST",
        !           508:                                             "LIST_LEFT",
        !           509:                                             "LIST_RIGHT",
        !           510:                                             "AFTER" };
        !           511:        size_t                   size = strlen(expanded);
        !           512:        struct screen           *os = octx->s, s[TOTAL];
        !           513:        struct screen_write_ctx  ctx[TOTAL];
        !           514:        u_int                    ocx = os->cx, ocy = os->cy, i, width[TOTAL];
        !           515:        u_int                    map[] = { LEFT, LEFT, CENTRE, RIGHT };
        !           516:        int                      focus_start = -1, focus_end = -1;
        !           517:        int                      list_state = -1;
        !           518:        enum style_align         list_align = STYLE_ALIGN_DEFAULT;
        !           519:        struct style             sy;
        !           520:        struct utf8_data        *ud = &sy.gc.data;
        !           521:        const char              *cp, *end;
        !           522:        enum utf8_state          more;
        !           523:        char                    *tmp;
        !           524:        struct format_range     *fr = NULL, *fr1;
        !           525:        struct format_ranges     frs;
        !           526:        struct style_range      *sr;
        !           527:
        !           528:        style_set(&sy, base);
        !           529:        TAILQ_INIT(&frs);
        !           530:
        !           531:        /*
        !           532:         * We build three screens for left, right, centre alignment, one for
        !           533:         * the list, one for anything after the list and two for the list left
        !           534:         * and right markers.
        !           535:         */
        !           536:        for (i = 0; i < TOTAL; i++) {
        !           537:                screen_init(&s[i], size, 1, 0);
        !           538:                screen_write_start(&ctx[i], NULL, &s[i]);
        !           539:                screen_write_clearendofline(&ctx[i], base->bg);
        !           540:                width[i] = 0;
        !           541:        }
        !           542:
        !           543:        /*
        !           544:         * Walk the string and add to the corresponding screens,
        !           545:         * parsing styles as we go.
        !           546:         */
        !           547:        cp = expanded;
        !           548:        while (*cp != '\0') {
        !           549:                if (cp[0] != '#' || cp[1] != '[') {
        !           550:                        /* See if this is a UTF-8 character. */
        !           551:                        if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
        !           552:                                while (*++cp != '\0' && more == UTF8_MORE)
        !           553:                                        more = utf8_append(ud, *cp);
        !           554:                                if (more != UTF8_DONE)
        !           555:                                        cp -= ud->have;
        !           556:                        }
        !           557:
        !           558:                        /* Not a UTF-8 character - ASCII or not valid. */
        !           559:                        if (more != UTF8_DONE) {
        !           560:                                if (*cp < 0x20 || *cp > 0x7e) {
        !           561:                                        /* Ignore nonprintable characters. */
        !           562:                                        cp++;
        !           563:                                        continue;
        !           564:                                }
        !           565:                                utf8_set(ud, *cp);
        !           566:                                cp++;
        !           567:                        }
        !           568:
        !           569:                        /* Draw the cell to th current screen. */
        !           570:                        screen_write_cell(&ctx[current], &sy.gc);
        !           571:                        width[current] += ud->width;
        !           572:                        continue;
        !           573:                }
        !           574:
        !           575:                /* This is a style. Work out where the end is and parse it. */
        !           576:                end = format_skip(cp + 2, "]");
        !           577:                if (end == NULL)
        !           578:                        return;
        !           579:                tmp = xstrndup(cp + 2, end - (cp + 2));
        !           580:                if (style_parse(&sy, base, tmp) != 0) {
        !           581:                        free(tmp);
        !           582:                        return;
        !           583:                }
        !           584:                log_debug("style '%s' -> '%s'", tmp, style_tostring(&sy));
        !           585:                free(tmp);
        !           586:
        !           587:                /* Check the list state. */
        !           588:                switch (sy.list) {
        !           589:                case STYLE_LIST_ON:
        !           590:                        /*
        !           591:                         * Entering the list, exiting a marker, or exiting the
        !           592:                         * focus.
        !           593:                         */
        !           594:                        if (list_state != 0) {
        !           595:                                if (fr != NULL) { /* abort any region */
        !           596:                                        free(fr);
        !           597:                                        fr = NULL;
        !           598:                                }
        !           599:                                list_state = 0;
        !           600:                                list_align = sy.align;
        !           601:                        }
        !           602:
        !           603:                        /* End the focus if started. */
        !           604:                        if (focus_start != -1 && focus_end == -1)
        !           605:                                focus_end = s[LIST].cx;
        !           606:
        !           607:                        current = LIST;
        !           608:                        break;
        !           609:                case STYLE_LIST_FOCUS:
        !           610:                        /* Entering the focus. */
        !           611:                        if (list_state != 0) /* not inside the list */
        !           612:                                break;
        !           613:                        if (focus_start == -1) /* focus already started */
        !           614:                                focus_start = s[LIST].cx;
        !           615:                        break;
        !           616:                case STYLE_LIST_OFF:
        !           617:                        /* Exiting or outside the list. */
        !           618:                        if (list_state == 0) {
        !           619:                                if (fr != NULL) { /* abort any region */
        !           620:                                        free(fr);
        !           621:                                        fr = NULL;
        !           622:                                }
        !           623:                                if (focus_start != -1 && focus_end == -1)
        !           624:                                        focus_end = s[LIST].cx;
        !           625:
        !           626:                                map[list_align] = AFTER;
        !           627:                                if (list_align == STYLE_ALIGN_LEFT)
        !           628:                                        map[STYLE_ALIGN_DEFAULT] = AFTER;
        !           629:                                list_state = 1;
        !           630:                        }
        !           631:                        current = map[sy.align];
        !           632:                        break;
        !           633:                case STYLE_LIST_LEFT_MARKER:
        !           634:                        /* Entering left marker. */
        !           635:                        if (list_state != 0) /* not inside the list */
        !           636:                                break;
        !           637:                        if (s[LIST_LEFT].cx != 0) /* already have marker */
        !           638:                                break;
        !           639:                        if (fr != NULL) { /* abort any region */
        !           640:                                free(fr);
        !           641:                                fr = NULL;
        !           642:                        }
        !           643:                        if (focus_start != -1 && focus_end == -1)
        !           644:                                focus_start = focus_end = -1;
        !           645:                        current = LIST_LEFT;
        !           646:                        break;
        !           647:                case STYLE_LIST_RIGHT_MARKER:
        !           648:                        /* Entering right marker. */
        !           649:                        if (list_state != 0) /* not inside the list */
        !           650:                                break;
        !           651:                        if (s[LIST_RIGHT].cx != 0) /* already have marker */
        !           652:                                break;
        !           653:                        if (fr != NULL) { /* abort any region */
        !           654:                                free(fr);
        !           655:                                fr = NULL;
        !           656:                        }
        !           657:                        if (focus_start != -1 && focus_end == -1)
        !           658:                                focus_start = focus_end = -1;
        !           659:                        current = LIST_RIGHT;
        !           660:                        break;
        !           661:                }
        !           662:                if (current != last) {
        !           663:                        log_debug("%s: change %s -> %s", __func__,
        !           664:                            names[last], names[current]);
        !           665:                        last = current;
        !           666:                }
        !           667:
        !           668:                /*
        !           669:                 * Check if the range style has changed and if so end the
        !           670:                 * current range and start a new one if needed.
        !           671:                 */
        !           672:                if (srs != NULL) {
        !           673:                        if (fr != NULL && !format_is_type(fr, &sy)) {
        !           674:                                if (s[current].cx != fr->start) {
        !           675:                                        fr->end = s[current].cx + 1;
        !           676:                                        TAILQ_INSERT_TAIL(&frs, fr, entry);
        !           677:                                } else
        !           678:                                        free(fr);
        !           679:                                fr = NULL;
        !           680:                        }
        !           681:                        if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) {
        !           682:                                fr = xcalloc(1, sizeof *fr);
        !           683:                                fr->index = current;
        !           684:
        !           685:                                fr->s = &s[current];
        !           686:                                fr->start = s[current].cx;
        !           687:
        !           688:                                fr->type = sy.range_type;
        !           689:                                fr->argument = sy.range_argument;
        !           690:                        }
        !           691:                }
        !           692:
        !           693:                cp = end + 1;
        !           694:        }
        !           695:        free(fr);
        !           696:
        !           697:        for (i = 0; i < TOTAL; i++)
        !           698:                log_debug("%s: width %s is %u", __func__, names[i], width[i]);
        !           699:        if (focus_start != -1 && focus_end != -1)
        !           700:                log_debug("focus is %d-%d", focus_start, focus_end);
        !           701:        TAILQ_FOREACH(fr, &frs, entry) {
        !           702:                log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type,
        !           703:                    fr->argument, names[fr->index], fr->start, fr->end);
        !           704:        }
        !           705:
        !           706:        /*
        !           707:         * Draw the screens. How they are arranged depends on where the list
        !           708:         * appearsq.
        !           709:         */
        !           710:        switch (list_align) {
        !           711:        case STYLE_ALIGN_DEFAULT:
        !           712:                /* No list. */
        !           713:                format_draw_none(octx, available, ocx, ocy, &s[LEFT],
        !           714:                    &s[CENTRE], &s[RIGHT], &frs);
        !           715:                break;
        !           716:        case STYLE_ALIGN_LEFT:
        !           717:                /* List is part of the left. */
        !           718:                format_draw_left(octx, available, ocx, ocy, &s[LEFT],
        !           719:                    &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
        !           720:                    &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
        !           721:                break;
        !           722:        case STYLE_ALIGN_CENTRE:
        !           723:                /* List is part of the centre. */
        !           724:                format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
        !           725:                    &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
        !           726:                    &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
        !           727:                break;
        !           728:        case STYLE_ALIGN_RIGHT:
        !           729:                /* List is part of the right. */
        !           730:                format_draw_right(octx, available, ocx, ocy, &s[LEFT],
        !           731:                    &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
        !           732:                    &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
        !           733:                break;
        !           734:        }
        !           735:
        !           736:        /* Create ranges to return. */
        !           737:        TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) {
        !           738:                sr = xcalloc(1, sizeof *sr);
        !           739:                sr->type = fr->type;
        !           740:                sr->argument = fr->argument;
        !           741:                sr->start = fr->start;
        !           742:                sr->end = fr->end;
        !           743:                TAILQ_INSERT_TAIL(srs, sr, entry);
        !           744:
        !           745:                log_debug("%s: range %d|%u at %u-%u", __func__, sr->type,
        !           746:                    sr->argument, sr->start, sr->end);
        !           747:
        !           748:                format_free_range(&frs, fr);
        !           749:        }
        !           750:
        !           751:        /* Restore the original cursor position. */
        !           752:        screen_write_cursormove(octx, ocx, ocy, 0);
        !           753: }
        !           754:
        !           755: /* Get width, taking #[] into account. */
        !           756: u_int
        !           757: format_width(const char *expanded)
        !           758: {
        !           759:        const char              *cp, *end;
        !           760:        u_int                    width = 0;
        !           761:        struct utf8_data         ud;
        !           762:        enum utf8_state          more;
        !           763:
        !           764:        cp = expanded;
        !           765:        while (*cp != '\0') {
        !           766:                if (cp[0] == '#' && cp[1] == '[') {
        !           767:                        end = format_skip(cp + 2, "]");
        !           768:                        if (end == NULL)
        !           769:                                return 0;
        !           770:                        cp = end + 1;
        !           771:                } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
        !           772:                        while (*++cp != '\0' && more == UTF8_MORE)
        !           773:                                more = utf8_append(&ud, *cp);
        !           774:                        if (more == UTF8_DONE)
        !           775:                                width += ud.width;
        !           776:                        else
        !           777:                                cp -= ud.have;
        !           778:                } else if (*cp > 0x1f && *cp < 0x7f) {
        !           779:                        width++;
        !           780:                        cp++;
        !           781:                }
        !           782:        }
        !           783:        return (width);
        !           784: }
        !           785:
        !           786: /* Trim on the left, taking #[] into account. */
        !           787: char *
        !           788: format_trim_left(const char *expanded, u_int limit)
        !           789: {
        !           790:        char                    *copy, *out;
        !           791:        const char              *cp = expanded, *end;
        !           792:        u_int                    width = 0;
        !           793:        struct utf8_data         ud;
        !           794:        enum utf8_state          more;
        !           795:
        !           796:        out = copy = xmalloc(strlen(expanded) + 1);
        !           797:        while (*cp != '\0') {
        !           798:                if (cp[0] == '#' && cp[1] == '[') {
        !           799:                        end = format_skip(cp + 2, "]");
        !           800:                        if (end == NULL)
        !           801:                                break;
        !           802:                        memcpy(out, cp, end + 1 - cp);
        !           803:                        out += (end + 1 - cp);
        !           804:                        cp = end + 1;
        !           805:                } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
        !           806:                        while (*++cp != '\0' && more == UTF8_MORE)
        !           807:                                more = utf8_append(&ud, *cp);
        !           808:                        if (more == UTF8_DONE) {
        !           809:                                if (width + ud.width <= limit) {
        !           810:                                        memcpy(out, ud.data, ud.size);
        !           811:                                        out += ud.size;
        !           812:                                }
        !           813:                                width += ud.width;
        !           814:                        } else
        !           815:                                cp -= ud.have;
        !           816:                } else if (*cp > 0x1f && *cp < 0x7f) {
        !           817:                        if (width + 1 <= limit)
        !           818:                                *out++ = *cp;
        !           819:                        width++;
        !           820:                        cp++;
        !           821:                }
        !           822:        }
        !           823:        *out = '\0';
        !           824:        return (copy);
        !           825: }
        !           826:
        !           827: /* Trim on the right, taking #[] into account. */
        !           828: char *
        !           829: format_trim_right(const char *expanded, u_int limit)
        !           830: {
        !           831:        char                    *copy, *out;
        !           832:        const char              *cp = expanded, *end;
        !           833:        u_int                    width = 0, total_width, skip;
        !           834:        struct utf8_data         ud;
        !           835:        enum utf8_state          more;
        !           836:
        !           837:        total_width = format_width(expanded);
        !           838:        if (total_width <= limit)
        !           839:                return (xstrdup(expanded));
        !           840:        skip = total_width - limit;
        !           841:
        !           842:        out = copy = xmalloc(strlen(expanded) + 1);
        !           843:        while (*cp != '\0') {
        !           844:                if (cp[0] == '#' && cp[1] == '[') {
        !           845:                        end = format_skip(cp + 2, "]");
        !           846:                        if (end == NULL)
        !           847:                                break;
        !           848:                        memcpy(out, cp, end + 1 - cp);
        !           849:                        out += (end + 1 - cp);
        !           850:                        cp = end + 1;
        !           851:                } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
        !           852:                        while (*++cp != '\0' && more == UTF8_MORE)
        !           853:                                more = utf8_append(&ud, *cp);
        !           854:                        if (more == UTF8_DONE) {
        !           855:                                if (width >= skip) {
        !           856:                                        memcpy(out, ud.data, ud.size);
        !           857:                                        out += ud.size;
        !           858:                                }
        !           859:                                width += ud.width;
        !           860:                        } else
        !           861:                                cp -= ud.have;
        !           862:                } else if (*cp > 0x1f && *cp < 0x7f) {
        !           863:                        if (width >= skip)
        !           864:                                *out++ = *cp;
        !           865:                        width++;
        !           866:                        cp++;
        !           867:                }
        !           868:        }
        !           869:        *out = '\0';
        !           870:        return (copy);
        !           871: }