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

Annotation of src/usr.bin/tmux/mode-tree.c, Revision 1.16

1.16    ! nicm        1: /* $OpenBSD: mode-tree.c,v 1.15 2017/10/25 12:13:20 nicm Exp $ */
1.1       nicm        2:
                      3: /*
                      4:  * Copyright (c) 2017 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 <ctype.h>
                     22: #include <stdio.h>
                     23: #include <stdlib.h>
                     24: #include <string.h>
                     25:
                     26: #include "tmux.h"
                     27:
                     28: struct mode_tree_item;
                     29: TAILQ_HEAD(mode_tree_list, mode_tree_item);
                     30:
                     31: struct mode_tree_data {
1.3       nicm       32:        int                       dead;
                     33:        u_int                     references;
                     34:
1.1       nicm       35:        struct window_pane       *wp;
                     36:        void                     *modedata;
                     37:
                     38:        const char              **sort_list;
                     39:        u_int                     sort_size;
                     40:        u_int                     sort_type;
                     41:
1.6       nicm       42:        void                     (*buildcb)(void *, u_int, uint64_t *,
                     43:                                     const char *);
1.1       nicm       44:        struct screen           *(*drawcb)(void *, void *, u_int, u_int);
1.3       nicm       45:        int                      (*searchcb)(void*, void *, const char *);
1.1       nicm       46:
                     47:        struct mode_tree_list     children;
                     48:        struct mode_tree_list     saved;
                     49:
                     50:        struct mode_tree_line    *line_list;
                     51:        u_int                     line_size;
                     52:
                     53:        u_int                     depth;
                     54:
                     55:        u_int                     width;
                     56:        u_int                     height;
                     57:
                     58:        u_int                     offset;
                     59:        u_int                     current;
                     60:
                     61:        struct screen             screen;
1.3       nicm       62:
1.9       nicm       63:        int                       preview;
1.6       nicm       64:        char                     *search;
                     65:        char                     *filter;
1.1       nicm       66: };
                     67:
                     68: struct mode_tree_item {
                     69:        struct mode_tree_item           *parent;
                     70:        void                            *itemdata;
                     71:        u_int                            line;
                     72:
                     73:        uint64_t                         tag;
                     74:        const char                      *name;
                     75:        const char                      *text;
                     76:
                     77:        int                              expanded;
                     78:        int                              tagged;
                     79:
                     80:        struct mode_tree_list            children;
                     81:        TAILQ_ENTRY(mode_tree_item)      entry;
                     82: };
                     83:
                     84: struct mode_tree_line {
                     85:        struct mode_tree_item           *item;
                     86:        u_int                            depth;
                     87:        int                              last;
                     88:        int                              flat;
                     89: };
                     90:
                     91: static void mode_tree_free_items(struct mode_tree_list *);
                     92:
                     93: static struct mode_tree_item *
                     94: mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
                     95: {
                     96:        struct mode_tree_item   *mti, *child;
                     97:
                     98:        TAILQ_FOREACH(mti, mtl, entry) {
                     99:                if (mti->tag == tag)
                    100:                        return (mti);
                    101:                child = mode_tree_find_item(&mti->children, tag);
                    102:                if (child != NULL)
                    103:                        return (child);
                    104:        }
                    105:        return (NULL);
                    106: }
                    107:
                    108: static void
                    109: mode_tree_free_item(struct mode_tree_item *mti)
                    110: {
                    111:        mode_tree_free_items(&mti->children);
                    112:
                    113:        free((void *)mti->name);
                    114:        free((void *)mti->text);
                    115:
                    116:        free(mti);
                    117: }
                    118:
                    119: static void
                    120: mode_tree_free_items(struct mode_tree_list *mtl)
                    121: {
                    122:        struct mode_tree_item   *mti, *mti1;
                    123:
                    124:        TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) {
                    125:                TAILQ_REMOVE(mtl, mti, entry);
                    126:                mode_tree_free_item(mti);
                    127:        }
                    128: }
                    129:
                    130: static void
1.11      nicm      131: mode_tree_check_selected(struct mode_tree_data *mtd)
                    132: {
                    133:        /*
                    134:         * If the current line would now be off screen reset the offset to the
                    135:         * last visible line.
                    136:         */
                    137:        if (mtd->current > mtd->height - 1)
                    138:                mtd->offset = mtd->current - mtd->height + 1;
                    139: }
                    140:
                    141: static void
1.1       nicm      142: mode_tree_clear_lines(struct mode_tree_data *mtd)
                    143: {
                    144:        free(mtd->line_list);
                    145:        mtd->line_list = NULL;
                    146:        mtd->line_size = 0;
                    147: }
                    148:
                    149: static void
                    150: mode_tree_build_lines(struct mode_tree_data *mtd,
                    151:     struct mode_tree_list *mtl, u_int depth)
                    152: {
                    153:        struct mode_tree_item   *mti;
                    154:        struct mode_tree_line   *line;
                    155:        u_int                    i;
                    156:        int                      flat = 1;
                    157:
                    158:        mtd->depth = depth;
                    159:        TAILQ_FOREACH(mti, mtl, entry) {
                    160:                mtd->line_list = xreallocarray(mtd->line_list,
                    161:                    mtd->line_size + 1, sizeof *mtd->line_list);
                    162:
                    163:                line = &mtd->line_list[mtd->line_size++];
                    164:                line->item = mti;
                    165:                line->depth = depth;
                    166:                line->last = (mti == TAILQ_LAST(mtl, mode_tree_list));
                    167:
                    168:                mti->line = (mtd->line_size - 1);
                    169:                if (!TAILQ_EMPTY(&mti->children))
                    170:                        flat = 0;
                    171:                if (mti->expanded)
                    172:                        mode_tree_build_lines(mtd, &mti->children, depth + 1);
                    173:        }
                    174:        TAILQ_FOREACH(mti, mtl, entry) {
                    175:                for (i = 0; i < mtd->line_size; i++) {
                    176:                        line = &mtd->line_list[i];
                    177:                        if (line->item == mti)
                    178:                                line->flat = flat;
                    179:                }
                    180:        }
                    181: }
                    182:
                    183: static void
                    184: mode_tree_clear_tagged(struct mode_tree_list *mtl)
                    185: {
                    186:        struct mode_tree_item   *mti;
                    187:
                    188:        TAILQ_FOREACH(mti, mtl, entry) {
                    189:                mti->tagged = 0;
                    190:                mode_tree_clear_tagged(&mti->children);
                    191:        }
                    192: }
                    193:
1.3       nicm      194: static void
                    195: mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
                    196: {
                    197:        u_int   i;
                    198:
                    199:        for (i = 0; i < mtd->line_size; i++) {
                    200:                if (mtd->line_list[i].item->tag == tag)
                    201:                        break;
                    202:        }
                    203:        if (i != mtd->line_size) {
                    204:                mtd->current = i;
                    205:                if (mtd->current > mtd->height - 1)
1.11      nicm      206:                        mtd->offset = mtd->current - mtd->height + 1;
1.3       nicm      207:                else
                    208:                        mtd->offset = 0;
                    209:        } else {
                    210:                mtd->current = 0;
                    211:                mtd->offset = 0;
                    212:        }
                    213: }
                    214:
1.1       nicm      215: void
                    216: mode_tree_up(struct mode_tree_data *mtd, int wrap)
                    217: {
                    218:        if (mtd->current == 0) {
                    219:                if (wrap) {
                    220:                        mtd->current = mtd->line_size - 1;
                    221:                        if (mtd->line_size >= mtd->height)
                    222:                                mtd->offset = mtd->line_size - mtd->height;
                    223:                }
                    224:        } else {
                    225:                mtd->current--;
                    226:                if (mtd->current < mtd->offset)
                    227:                        mtd->offset--;
                    228:        }
                    229: }
                    230:
                    231: void
                    232: mode_tree_down(struct mode_tree_data *mtd, int wrap)
                    233: {
                    234:        if (mtd->current == mtd->line_size - 1) {
                    235:                if (wrap) {
                    236:                        mtd->current = 0;
                    237:                        mtd->offset = 0;
                    238:                }
                    239:        } else {
                    240:                mtd->current++;
                    241:                if (mtd->current > mtd->offset + mtd->height - 1)
                    242:                        mtd->offset++;
                    243:        }
                    244: }
                    245:
                    246: void *
                    247: mode_tree_get_current(struct mode_tree_data *mtd)
                    248: {
                    249:        return (mtd->line_list[mtd->current].item->itemdata);
                    250: }
                    251:
                    252: u_int
                    253: mode_tree_count_tagged(struct mode_tree_data *mtd)
                    254: {
                    255:        struct mode_tree_item   *mti;
                    256:        u_int                    i, tagged;
                    257:
                    258:        tagged = 0;
                    259:        for (i = 0; i < mtd->line_size; i++) {
                    260:                mti = mtd->line_list[i].item;
                    261:                if (mti->tagged)
                    262:                        tagged++;
                    263:        }
                    264:        return (tagged);
                    265: }
                    266:
                    267: void
                    268: mode_tree_each_tagged(struct mode_tree_data *mtd, void (*cb)(void *, void *,
1.14      nicm      269:     struct client *, key_code), struct client *c, key_code key, int current)
1.1       nicm      270: {
                    271:        struct mode_tree_item   *mti;
                    272:        u_int                    i;
                    273:        int                      fired;
                    274:
                    275:        fired = 0;
                    276:        for (i = 0; i < mtd->line_size; i++) {
                    277:                mti = mtd->line_list[i].item;
                    278:                if (mti->tagged) {
                    279:                        fired = 1;
1.14      nicm      280:                        cb(mtd->modedata, mti->itemdata, c, key);
1.1       nicm      281:                }
                    282:        }
                    283:        if (!fired && current) {
                    284:                mti = mtd->line_list[mtd->current].item;
1.14      nicm      285:                cb(mtd->modedata, mti->itemdata, c, key);
1.1       nicm      286:        }
                    287: }
                    288:
                    289: struct mode_tree_data *
1.5       nicm      290: mode_tree_start(struct window_pane *wp, struct args *args,
1.6       nicm      291:     void (*buildcb)(void *, u_int, uint64_t *, const char *),
1.5       nicm      292:     struct screen *(*drawcb)(void *, void *, u_int, u_int),
1.3       nicm      293:     int (*searchcb)(void *, void *, const char *), void *modedata,
                    294:     const char **sort_list, u_int sort_size, struct screen **s)
1.1       nicm      295: {
                    296:        struct mode_tree_data   *mtd;
1.5       nicm      297:        const char              *sort;
                    298:        u_int                    i;
1.1       nicm      299:
                    300:        mtd = xcalloc(1, sizeof *mtd);
1.3       nicm      301:        mtd->references = 1;
                    302:
1.1       nicm      303:        mtd->wp = wp;
                    304:        mtd->modedata = modedata;
                    305:
                    306:        mtd->sort_list = sort_list;
                    307:        mtd->sort_size = sort_size;
                    308:        mtd->sort_type = 0;
1.5       nicm      309:
1.9       nicm      310:        mtd->preview = !args_has(args, 'N');
                    311:
1.5       nicm      312:        sort = args_get(args, 'O');
                    313:        if (sort != NULL) {
                    314:                for (i = 0; i < sort_size; i++) {
                    315:                        if (strcasecmp(sort, sort_list[i]) == 0)
                    316:                                mtd->sort_type = i;
                    317:                }
                    318:        }
1.1       nicm      319:
1.6       nicm      320:        if (args_has(args, 'f'))
                    321:                mtd->filter = xstrdup(args_get(args, 'f'));
                    322:        else
                    323:                mtd->filter = NULL;
                    324:
1.1       nicm      325:        mtd->buildcb = buildcb;
                    326:        mtd->drawcb = drawcb;
1.3       nicm      327:        mtd->searchcb = searchcb;
1.1       nicm      328:
                    329:        TAILQ_INIT(&mtd->children);
                    330:
                    331:        *s = &mtd->screen;
                    332:        screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
                    333:        (*s)->mode &= ~MODE_CURSOR;
                    334:
                    335:        return (mtd);
                    336: }
                    337:
                    338: void
                    339: mode_tree_build(struct mode_tree_data *mtd)
                    340: {
                    341:        struct screen   *s = &mtd->screen;
                    342:        uint64_t         tag;
                    343:
                    344:        if (mtd->line_list != NULL)
                    345:                tag = mtd->line_list[mtd->current].item->tag;
                    346:        else
                    347:                tag = 0;
                    348:
                    349:        TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
                    350:        TAILQ_INIT(&mtd->children);
                    351:
1.6       nicm      352:        mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter);
                    353:        if (TAILQ_EMPTY(&mtd->children))
                    354:                mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL);
1.1       nicm      355:
                    356:        mode_tree_free_items(&mtd->saved);
                    357:        TAILQ_INIT(&mtd->saved);
                    358:
                    359:        mode_tree_clear_lines(mtd);
                    360:        mode_tree_build_lines(mtd, &mtd->children, 0);
                    361:
1.3       nicm      362:        mode_tree_set_current(mtd, tag);
1.1       nicm      363:
                    364:        mtd->width = screen_size_x(s);
1.9       nicm      365:        if (mtd->preview) {
                    366:                mtd->height = (screen_size_y(s) / 3) * 2;
                    367:                if (mtd->height > mtd->line_size)
                    368:                        mtd->height = screen_size_y(s) / 2;
                    369:                if (mtd->height < 10)
                    370:                        mtd->height = screen_size_y(s);
                    371:                if (screen_size_y(s) - mtd->height < 2)
                    372:                        mtd->height = screen_size_y(s);
                    373:        } else
1.1       nicm      374:                mtd->height = screen_size_y(s);
1.11      nicm      375:        mode_tree_check_selected(mtd);
1.1       nicm      376: }
                    377:
1.3       nicm      378: static void
                    379: mode_tree_remove_ref(struct mode_tree_data *mtd)
                    380: {
                    381:        if (--mtd->references == 0)
                    382:                free(mtd);
                    383: }
                    384:
1.1       nicm      385: void
                    386: mode_tree_free(struct mode_tree_data *mtd)
                    387: {
                    388:        mode_tree_free_items(&mtd->children);
                    389:        mode_tree_clear_lines(mtd);
                    390:        screen_free(&mtd->screen);
1.3       nicm      391:
1.6       nicm      392:        free(mtd->search);
                    393:        free(mtd->filter);
                    394:
1.3       nicm      395:        mtd->dead = 1;
                    396:        mode_tree_remove_ref(mtd);
1.1       nicm      397: }
                    398:
                    399: void
                    400: mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
                    401: {
                    402:        struct screen   *s = &mtd->screen;
                    403:
                    404:        screen_resize(s, sx, sy, 0);
                    405:
                    406:        mode_tree_build(mtd);
                    407:        mode_tree_draw(mtd);
                    408:
                    409:        mtd->wp->flags |= PANE_REDRAW;
                    410: }
                    411:
                    412: struct mode_tree_item *
                    413: mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
                    414:     void *itemdata, uint64_t tag, const char *name, const char *text,
                    415:     int expanded)
                    416: {
                    417:        struct mode_tree_item   *mti, *saved;
                    418:
                    419:        log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
                    420:            name, text);
                    421:
                    422:        mti = xcalloc(1, sizeof *mti);
                    423:        mti->parent = parent;
                    424:        mti->itemdata = itemdata;
                    425:
                    426:        mti->tag = tag;
                    427:        mti->name = xstrdup(name);
                    428:        mti->text = xstrdup(text);
                    429:
                    430:        saved = mode_tree_find_item(&mtd->saved, tag);
                    431:        if (saved != NULL) {
                    432:                if (parent == NULL || (parent != NULL && parent->expanded))
                    433:                        mti->tagged = saved->tagged;
                    434:                mti->expanded = saved->expanded;
                    435:        } else if (expanded == -1)
                    436:                mti->expanded = 1;
                    437:        else
                    438:                mti->expanded = expanded;
                    439:
                    440:        TAILQ_INIT(&mti->children);
                    441:
                    442:        if (parent != NULL)
                    443:                TAILQ_INSERT_TAIL(&parent->children, mti, entry);
                    444:        else
                    445:                TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
                    446:
                    447:        return (mti);
                    448: }
                    449:
                    450: void
                    451: mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
                    452: {
                    453:        struct mode_tree_item   *parent = mti->parent;
                    454:
                    455:        if (parent != NULL)
                    456:                TAILQ_REMOVE(&parent->children, mti, entry);
                    457:        else
                    458:                TAILQ_REMOVE(&mtd->children, mti, entry);
                    459:        mode_tree_free_item(mti);
                    460: }
                    461:
                    462: void
                    463: mode_tree_draw(struct mode_tree_data *mtd)
                    464: {
                    465:        struct window_pane      *wp = mtd->wp;
1.7       nicm      466:        struct screen           *s = &mtd->screen, *box = NULL;
1.1       nicm      467:        struct mode_tree_line   *line;
                    468:        struct mode_tree_item   *mti;
                    469:        struct options          *oo = wp->window->options;
                    470:        struct screen_write_ctx  ctx;
                    471:        struct grid_cell         gc0, gc;
                    472:        u_int                    w, h, i, j, sy, box_x, box_y;
                    473:        char                    *text, *start, key[7];
                    474:        const char              *tag, *symbol;
                    475:        size_t                   size;
                    476:        int                      keylen;
                    477:
                    478:        if (mtd->line_size == 0)
                    479:                return;
                    480:
                    481:        memcpy(&gc0, &grid_default_cell, sizeof gc0);
                    482:        memcpy(&gc, &grid_default_cell, sizeof gc);
                    483:        style_apply(&gc, oo, "mode-style");
                    484:
                    485:        w = mtd->width;
                    486:        h = mtd->height;
                    487:
                    488:        screen_write_start(&ctx, NULL, s);
                    489:        screen_write_clearscreen(&ctx, 8);
                    490:
                    491:        if (mtd->line_size > 10)
                    492:                keylen = 6;
                    493:        else
                    494:                keylen = 4;
                    495:
                    496:        for (i = 0; i < mtd->line_size; i++) {
                    497:                if (i < mtd->offset)
                    498:                        continue;
                    499:                if (i > mtd->offset + h - 1)
                    500:                        break;
                    501:
                    502:                line = &mtd->line_list[i];
                    503:                mti = line->item;
                    504:
                    505:                screen_write_cursormove(&ctx, 0, i - mtd->offset);
                    506:
                    507:                if (i < 10)
1.8       nicm      508:                        snprintf(key, sizeof key, "(%c)  ", '0' + i);
1.1       nicm      509:                else if (i < 36)
                    510:                        snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
                    511:                else
                    512:                        *key = '\0';
                    513:
                    514:                if (line->flat)
                    515:                        symbol = "";
                    516:                else if (TAILQ_EMPTY(&mti->children))
                    517:                        symbol = "  ";
                    518:                else if (mti->expanded)
                    519:                        symbol = "- ";
                    520:                else
                    521:                        symbol = "+ ";
                    522:
                    523:                if (line->depth == 0)
                    524:                        start = xstrdup(symbol);
                    525:                else {
                    526:                        size = (4 * line->depth) + 32;
                    527:
                    528:                        start = xcalloc(1, size);
                    529:                        for (j = 1; j < line->depth; j++) {
                    530:                                if (mti->parent != NULL &&
                    531:                                    mtd->line_list[mti->parent->line].last)
                    532:                                        strlcat(start, "    ", size);
                    533:                                else
                    534:                                        strlcat(start, "\001x\001   ", size);
                    535:                        }
                    536:                        if (line->last)
                    537:                                strlcat(start, "\001mq\001> ", size);
                    538:                        else
                    539:                                strlcat(start, "\001tq\001> ", size);
                    540:                        strlcat(start, symbol, size);
                    541:                }
                    542:
                    543:                if (mti->tagged)
                    544:                        tag = "*";
                    545:                else
                    546:                        tag = "";
                    547:                xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start,
                    548:                    mti->name, tag, mti->text);
                    549:                free(start);
                    550:
                    551:                if (mti->tagged) {
                    552:                        gc.attr ^= GRID_ATTR_BRIGHT;
                    553:                        gc0.attr ^= GRID_ATTR_BRIGHT;
                    554:                }
                    555:
                    556:                if (i != mtd->current) {
1.13      nicm      557:                        screen_write_nputs(&ctx, w, &gc0, "%s", text);
1.1       nicm      558:                        screen_write_clearendofline(&ctx, 8);
1.13      nicm      559:                } else {
                    560:                        screen_write_nputs(&ctx, w, &gc, "%s", text);
                    561:                        screen_write_clearendofline(&ctx, gc.bg);
                    562:                }
1.1       nicm      563:                free(text);
                    564:
                    565:                if (mti->tagged) {
                    566:                        gc.attr ^= GRID_ATTR_BRIGHT;
                    567:                        gc0.attr ^= GRID_ATTR_BRIGHT;
                    568:                }
                    569:        }
                    570:
                    571:        sy = screen_size_y(s);
1.9       nicm      572:        if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
1.1       nicm      573:                screen_write_stop(&ctx);
                    574:                return;
                    575:        }
                    576:
                    577:        line = &mtd->line_list[mtd->current];
                    578:        mti = line->item;
                    579:
                    580:        screen_write_cursormove(&ctx, 0, h);
                    581:        screen_write_box(&ctx, w, sy - h);
                    582:
                    583:        xasprintf(&text, " %s (sort: %s) ", mti->name,
                    584:            mtd->sort_list[mtd->sort_type]);
                    585:        if (w - 2 >= strlen(text)) {
                    586:                screen_write_cursormove(&ctx, 1, h);
                    587:                screen_write_puts(&ctx, &gc0, "%s", text);
                    588:        }
                    589:        free(text);
                    590:
                    591:        box_x = w - 4;
                    592:        box_y = sy - h - 2;
                    593:
1.7       nicm      594:        if (box_x != 0 && box_y != 0)
                    595:                box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y);
1.1       nicm      596:        if (box != NULL) {
                    597:                screen_write_cursormove(&ctx, 2, h + 1);
1.16    ! nicm      598:                screen_write_fast_copy(&ctx, box, 0, 0, box_x, box_y);
1.1       nicm      599:
                    600:                screen_free(box);
                    601:        }
                    602:
                    603:        screen_write_stop(&ctx);
                    604: }
                    605:
1.3       nicm      606: static struct mode_tree_item *
                    607: mode_tree_search_for(struct mode_tree_data *mtd)
                    608: {
                    609:        struct mode_tree_item   *mti, *last, *next;
                    610:
1.6       nicm      611:        if (mtd->search == NULL)
1.3       nicm      612:                return (NULL);
                    613:
                    614:        mti = last = mtd->line_list[mtd->current].item;
                    615:        for (;;) {
                    616:                if (!TAILQ_EMPTY(&mti->children))
                    617:                        mti = TAILQ_FIRST(&mti->children);
                    618:                else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
                    619:                        mti = next;
                    620:                else {
                    621:                        for (;;) {
                    622:                                mti = mti->parent;
                    623:                                if (mti == NULL)
                    624:                                        break;
                    625:                                if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
                    626:                                        mti = next;
                    627:                                        break;
                    628:                                }
                    629:                        }
                    630:                }
                    631:                if (mti == NULL)
                    632:                        mti = TAILQ_FIRST(&mtd->children);
                    633:                if (mti == last)
                    634:                        break;
                    635:
                    636:                if (mtd->searchcb == NULL) {
1.6       nicm      637:                        if (strstr(mti->name, mtd->search) != NULL)
1.3       nicm      638:                                return (mti);
                    639:                        continue;
                    640:                }
1.6       nicm      641:                if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
1.4       nicm      642:                        return (mti);
1.3       nicm      643:        }
                    644:        return (NULL);
                    645: }
                    646:
                    647: static void
                    648: mode_tree_search_set(struct mode_tree_data *mtd)
                    649: {
                    650:        struct mode_tree_item   *mti, *loop;
                    651:        uint64_t                 tag;
                    652:
                    653:        mti = mode_tree_search_for(mtd);
                    654:        if (mti == NULL)
                    655:                return;
                    656:        tag = mti->tag;
                    657:
                    658:        loop = mti->parent;
                    659:        while (loop != NULL) {
                    660:                loop->expanded = 1;
                    661:                loop = loop->parent;
                    662:        }
1.6       nicm      663:
1.3       nicm      664:        mode_tree_build(mtd);
                    665:        mode_tree_set_current(mtd, tag);
                    666:        mode_tree_draw(mtd);
1.6       nicm      667:        mtd->wp->flags |= PANE_REDRAW;
1.3       nicm      668: }
                    669:
                    670: static int
                    671: mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
                    672:     __unused int done)
                    673: {
                    674:        struct mode_tree_data   *mtd = data;
                    675:
                    676:        if (mtd->dead)
                    677:                return (0);
                    678:
1.6       nicm      679:        free(mtd->search);
                    680:        if (s == NULL || *s == '\0') {
                    681:                mtd->search = NULL;
1.3       nicm      682:                return (0);
                    683:        }
1.6       nicm      684:        mtd->search = xstrdup(s);
1.3       nicm      685:        mode_tree_search_set(mtd);
                    686:
                    687:        return (0);
                    688: }
                    689:
                    690: static void
                    691: mode_tree_search_free(void *data)
                    692: {
                    693:        mode_tree_remove_ref(data);
                    694: }
                    695:
1.6       nicm      696: static int
                    697: mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
                    698:     __unused int done)
                    699: {
                    700:        struct mode_tree_data   *mtd = data;
                    701:
                    702:        if (mtd->dead)
                    703:                return (0);
                    704:
                    705:        if (mtd->filter != NULL)
                    706:                free(mtd->filter);
                    707:        if (s == NULL || *s == '\0')
                    708:                mtd->filter = NULL;
                    709:        else
                    710:                mtd->filter = xstrdup(s);
                    711:
                    712:        mode_tree_build(mtd);
                    713:        mode_tree_draw(mtd);
                    714:        mtd->wp->flags |= PANE_REDRAW;
                    715:
                    716:        return (0);
                    717: }
                    718:
                    719: static void
                    720: mode_tree_filter_free(void *data)
                    721: {
                    722:        mode_tree_remove_ref(data);
                    723: }
                    724:
1.1       nicm      725: int
1.3       nicm      726: mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
                    727:     struct mouse_event *m)
1.1       nicm      728: {
                    729:        struct mode_tree_line   *line;
                    730:        struct mode_tree_item   *current, *parent;
                    731:        u_int                    i, x, y;
                    732:        int                      choice;
                    733:        key_code                 tmp;
                    734:
                    735:        if (*key == KEYC_MOUSEDOWN1_PANE) {
                    736:                if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
                    737:                        *key = KEYC_NONE;
                    738:                        return (0);
                    739:                }
                    740:                if (x > mtd->width || y > mtd->height) {
                    741:                        *key = KEYC_NONE;
                    742:                        return (0);
                    743:                }
                    744:                if (mtd->offset + y < mtd->line_size) {
                    745:                        mtd->current = mtd->offset + y;
                    746:                        *key = '\r';
                    747:                        return (0);
                    748:                }
                    749:        }
                    750:
                    751:        line = &mtd->line_list[mtd->current];
                    752:        current = line->item;
                    753:
                    754:        choice = -1;
                    755:        if (*key >= '0' && *key <= '9')
                    756:                choice = (*key) - '0';
                    757:        else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
                    758:                tmp = (*key) & KEYC_MASK_KEY;
                    759:                if (tmp >= 'a' && tmp <= 'z')
                    760:                        choice = 10 + (tmp - 'a');
                    761:        }
                    762:        if (choice != -1) {
                    763:                if ((u_int)choice > mtd->line_size - 1) {
                    764:                        *key = KEYC_NONE;
                    765:                        return (0);
                    766:                }
                    767:                mtd->current = choice;
                    768:                *key = '\r';
                    769:                return (0);
                    770:        }
                    771:
                    772:        switch (*key) {
                    773:        case 'q':
                    774:        case '\033': /* Escape */
                    775:                return (1);
                    776:        case KEYC_UP:
                    777:        case 'k':
                    778:        case KEYC_WHEELUP_PANE:
1.12      nicm      779:        case '\020': /* C-p */
1.1       nicm      780:                mode_tree_up(mtd, 1);
                    781:                break;
                    782:        case KEYC_DOWN:
                    783:        case 'j':
                    784:        case KEYC_WHEELDOWN_PANE:
1.12      nicm      785:        case '\016': /* C-n */
1.1       nicm      786:                mode_tree_down(mtd, 1);
                    787:                break;
                    788:        case KEYC_PPAGE:
                    789:        case '\002': /* C-b */
                    790:                for (i = 0; i < mtd->height; i++) {
                    791:                        if (mtd->current == 0)
                    792:                                break;
                    793:                        mode_tree_up(mtd, 1);
                    794:                }
                    795:                break;
                    796:        case KEYC_NPAGE:
                    797:        case '\006': /* C-f */
                    798:                for (i = 0; i < mtd->height; i++) {
                    799:                        if (mtd->current == mtd->line_size - 1)
                    800:                                break;
                    801:                        mode_tree_down(mtd, 1);
                    802:                }
                    803:                break;
                    804:        case KEYC_HOME:
                    805:                mtd->current = 0;
                    806:                mtd->offset = 0;
                    807:                break;
                    808:        case KEYC_END:
                    809:                mtd->current = mtd->line_size - 1;
                    810:                if (mtd->current > mtd->height - 1)
1.11      nicm      811:                        mtd->offset = mtd->current - mtd->height + 1;
1.1       nicm      812:                else
                    813:                        mtd->offset = 0;
                    814:                break;
                    815:        case 't':
                    816:                /*
                    817:                 * Do not allow parents and children to both be tagged: untag
                    818:                 * all parents and children of current.
                    819:                 */
                    820:                if (!current->tagged) {
                    821:                        parent = current->parent;
                    822:                        while (parent != NULL) {
                    823:                                parent->tagged = 0;
                    824:                                parent = parent->parent;
                    825:                        }
                    826:                        mode_tree_clear_tagged(&current->children);
                    827:                        current->tagged = 1;
                    828:                } else
                    829:                        current->tagged = 0;
                    830:                mode_tree_down(mtd, 0);
                    831:                break;
                    832:        case 'T':
                    833:                for (i = 0; i < mtd->line_size; i++)
                    834:                        mtd->line_list[i].item->tagged = 0;
                    835:                break;
                    836:        case '\024': /* C-t */
                    837:                for (i = 0; i < mtd->line_size; i++) {
                    838:                        if (mtd->line_list[i].item->parent == NULL)
                    839:                                mtd->line_list[i].item->tagged = 1;
                    840:                        else
                    841:                                mtd->line_list[i].item->tagged = 0;
                    842:                }
                    843:                break;
                    844:        case 'O':
                    845:                mtd->sort_type++;
                    846:                if (mtd->sort_type == mtd->sort_size)
                    847:                        mtd->sort_type = 0;
                    848:                mode_tree_build(mtd);
                    849:                break;
                    850:        case KEYC_LEFT:
1.15      nicm      851:        case 'h':
1.1       nicm      852:        case '-':
                    853:                if (line->flat || !current->expanded)
                    854:                        current = current->parent;
                    855:                if (current == NULL)
                    856:                        mode_tree_up(mtd, 0);
                    857:                else {
                    858:                        current->expanded = 0;
                    859:                        mtd->current = current->line;
                    860:                        mode_tree_build(mtd);
                    861:                }
                    862:                break;
                    863:        case KEYC_RIGHT:
1.15      nicm      864:        case 'l':
1.1       nicm      865:        case '+':
                    866:                if (line->flat || current->expanded)
                    867:                        mode_tree_down(mtd, 0);
                    868:                else if (!line->flat) {
                    869:                        current->expanded = 1;
                    870:                        mode_tree_build(mtd);
                    871:                }
1.3       nicm      872:                break;
                    873:        case '\023': /* C-s */
                    874:                mtd->references++;
                    875:                status_prompt_set(c, "(search) ", "",
                    876:                    mode_tree_search_callback, mode_tree_search_free, mtd,
                    877:                    PROMPT_NOFORMAT);
                    878:                break;
                    879:        case 'n':
                    880:                mode_tree_search_set(mtd);
1.6       nicm      881:                break;
                    882:        case 'f':
                    883:                mtd->references++;
                    884:                status_prompt_set(c, "(filter) ", mtd->filter,
                    885:                    mode_tree_filter_callback, mode_tree_filter_free, mtd,
                    886:                    PROMPT_NOFORMAT);
1.9       nicm      887:                break;
                    888:        case 'v':
                    889:                mtd->preview = !mtd->preview;
                    890:                mode_tree_build(mtd);
1.11      nicm      891:                if (mtd->preview)
                    892:                        mode_tree_check_selected(mtd);
1.1       nicm      893:                break;
                    894:        }
                    895:        return (0);
                    896: }
                    897:
                    898: void
                    899: mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
                    900:     const char *template, const char *name)
                    901: {
                    902:        struct cmdq_item        *new_item;
                    903:        struct cmd_list         *cmdlist;
                    904:        char                    *command, *cause;
                    905:
                    906:        command = cmd_template_replace(template, name, 1);
1.2       nicm      907:        if (command == NULL || *command == '\0') {
                    908:                free(command);
1.1       nicm      909:                return;
1.2       nicm      910:        }
1.1       nicm      911:
                    912:        cmdlist = cmd_string_parse(command, NULL, 0, &cause);
                    913:        if (cmdlist == NULL) {
                    914:                if (cause != NULL && c != NULL) {
                    915:                        *cause = toupper((u_char)*cause);
                    916:                        status_message_set(c, "%s", cause);
                    917:                }
                    918:                free(cause);
                    919:        } else {
                    920:                new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
                    921:                cmdq_append(c, new_item);
                    922:                cmd_list_free(cmdlist);
                    923:        }
                    924:
                    925:        free(command);
                    926: }