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

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