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

1.52    ! nicm        1: /* $OpenBSD: mode-tree.c,v 1.51 2020/07/27 08:03:10 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;
1.23      nicm       34:        int                       zoomed;
1.3       nicm       35:
1.1       nicm       36:        struct window_pane       *wp;
                     37:        void                     *modedata;
1.33      nicm       38:        const struct menu_item   *menu;
1.1       nicm       39:
                     40:        const char              **sort_list;
                     41:        u_int                     sort_size;
1.36      nicm       42:        struct mode_tree_sort_criteria sort_crit;
1.1       nicm       43:
1.17      nicm       44:        mode_tree_build_cb        buildcb;
                     45:        mode_tree_draw_cb         drawcb;
                     46:        mode_tree_search_cb       searchcb;
1.28      nicm       47:        mode_tree_menu_cb         menucb;
1.46      nicm       48:        mode_tree_height_cb       heightcb;
1.52    ! nicm       49:        mode_tree_key_cb          keycb;
1.1       nicm       50:
                     51:        struct mode_tree_list     children;
                     52:        struct mode_tree_list     saved;
                     53:
                     54:        struct mode_tree_line    *line_list;
                     55:        u_int                     line_size;
                     56:
                     57:        u_int                     depth;
                     58:
                     59:        u_int                     width;
                     60:        u_int                     height;
                     61:
                     62:        u_int                     offset;
                     63:        u_int                     current;
                     64:
                     65:        struct screen             screen;
1.3       nicm       66:
1.9       nicm       67:        int                       preview;
1.6       nicm       68:        char                     *search;
                     69:        char                     *filter;
1.21      nicm       70:        int                       no_matches;
1.1       nicm       71: };
                     72:
                     73: struct mode_tree_item {
                     74:        struct mode_tree_item           *parent;
                     75:        void                            *itemdata;
                     76:        u_int                            line;
                     77:
1.52    ! nicm       78:        key_code                         key;
        !            79:        const char                      *keystr;
        !            80:        size_t                           keylen;
        !            81:
1.1       nicm       82:        uint64_t                         tag;
                     83:        const char                      *name;
                     84:        const char                      *text;
                     85:
                     86:        int                              expanded;
                     87:        int                              tagged;
1.50      nicm       88:
1.46      nicm       89:        int                              draw_as_parent;
1.50      nicm       90:        int                              no_tag;
1.1       nicm       91:
                     92:        struct mode_tree_list            children;
                     93:        TAILQ_ENTRY(mode_tree_item)      entry;
                     94: };
                     95:
                     96: struct mode_tree_line {
                     97:        struct mode_tree_item           *item;
                     98:        u_int                            depth;
                     99:        int                              last;
                    100:        int                              flat;
                    101: };
                    102:
1.28      nicm      103: struct mode_tree_menu {
                    104:        struct mode_tree_data           *data;
                    105:        struct client                   *c;
                    106:        u_int                            line;
                    107:        void                            *itemdata;
                    108: };
                    109:
1.1       nicm      110: static void mode_tree_free_items(struct mode_tree_list *);
                    111:
1.33      nicm      112: static const struct menu_item mode_tree_menu_items[] = {
                    113:        { "Scroll Left", '<', NULL },
                    114:        { "Scroll Right", '>', NULL },
                    115:        { "", KEYC_NONE, NULL },
                    116:        { "Cancel", 'q', NULL },
                    117:
                    118:        { NULL, KEYC_NONE, NULL }
                    119: };
1.28      nicm      120:
1.1       nicm      121: static struct mode_tree_item *
                    122: mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
                    123: {
                    124:        struct mode_tree_item   *mti, *child;
                    125:
                    126:        TAILQ_FOREACH(mti, mtl, entry) {
                    127:                if (mti->tag == tag)
                    128:                        return (mti);
                    129:                child = mode_tree_find_item(&mti->children, tag);
                    130:                if (child != NULL)
                    131:                        return (child);
                    132:        }
                    133:        return (NULL);
                    134: }
                    135:
                    136: static void
                    137: mode_tree_free_item(struct mode_tree_item *mti)
                    138: {
                    139:        mode_tree_free_items(&mti->children);
                    140:
                    141:        free((void *)mti->name);
                    142:        free((void *)mti->text);
1.52    ! nicm      143:        free((void *)mti->keystr);
1.1       nicm      144:
                    145:        free(mti);
                    146: }
                    147:
                    148: static void
                    149: mode_tree_free_items(struct mode_tree_list *mtl)
                    150: {
                    151:        struct mode_tree_item   *mti, *mti1;
                    152:
                    153:        TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) {
                    154:                TAILQ_REMOVE(mtl, mti, entry);
                    155:                mode_tree_free_item(mti);
                    156:        }
                    157: }
                    158:
                    159: static void
1.11      nicm      160: mode_tree_check_selected(struct mode_tree_data *mtd)
                    161: {
                    162:        /*
                    163:         * If the current line would now be off screen reset the offset to the
                    164:         * last visible line.
                    165:         */
                    166:        if (mtd->current > mtd->height - 1)
                    167:                mtd->offset = mtd->current - mtd->height + 1;
                    168: }
                    169:
                    170: static void
1.1       nicm      171: mode_tree_clear_lines(struct mode_tree_data *mtd)
                    172: {
                    173:        free(mtd->line_list);
                    174:        mtd->line_list = NULL;
                    175:        mtd->line_size = 0;
                    176: }
                    177:
                    178: static void
                    179: mode_tree_build_lines(struct mode_tree_data *mtd,
                    180:     struct mode_tree_list *mtl, u_int depth)
                    181: {
                    182:        struct mode_tree_item   *mti;
                    183:        struct mode_tree_line   *line;
                    184:        u_int                    i;
                    185:        int                      flat = 1;
                    186:
                    187:        mtd->depth = depth;
                    188:        TAILQ_FOREACH(mti, mtl, entry) {
                    189:                mtd->line_list = xreallocarray(mtd->line_list,
                    190:                    mtd->line_size + 1, sizeof *mtd->line_list);
                    191:
                    192:                line = &mtd->line_list[mtd->line_size++];
                    193:                line->item = mti;
                    194:                line->depth = depth;
                    195:                line->last = (mti == TAILQ_LAST(mtl, mode_tree_list));
                    196:
                    197:                mti->line = (mtd->line_size - 1);
                    198:                if (!TAILQ_EMPTY(&mti->children))
                    199:                        flat = 0;
                    200:                if (mti->expanded)
                    201:                        mode_tree_build_lines(mtd, &mti->children, depth + 1);
1.52    ! nicm      202:
        !           203:                if (mtd->keycb != NULL) {
        !           204:                        mti->key = mtd->keycb(mtd->modedata, mti->itemdata,
        !           205:                            mti->line);
        !           206:                        if (mti->key == KEYC_UNKNOWN)
        !           207:                                mti->key = KEYC_NONE;
        !           208:                } else if (mti->line < 10)
        !           209:                        mti->key = '0' + mti->line;
        !           210:                else if (mti->line < 36)
        !           211:                        mti->key = KEYC_META|('a' + mti->line - 10);
        !           212:                else
        !           213:                        mti->key = KEYC_NONE;
        !           214:                if (mti->key != KEYC_NONE) {
        !           215:                        mti->keystr = xstrdup(key_string_lookup_key(mti->key,
        !           216:                            0));
        !           217:                        mti->keylen = strlen(mti->keystr);
        !           218:                } else {
        !           219:                        mti->keystr = NULL;
        !           220:                        mti->keylen = 0;
        !           221:                }
1.1       nicm      222:        }
                    223:        TAILQ_FOREACH(mti, mtl, entry) {
                    224:                for (i = 0; i < mtd->line_size; i++) {
                    225:                        line = &mtd->line_list[i];
                    226:                        if (line->item == mti)
                    227:                                line->flat = flat;
                    228:                }
                    229:        }
                    230: }
                    231:
                    232: static void
                    233: mode_tree_clear_tagged(struct mode_tree_list *mtl)
                    234: {
                    235:        struct mode_tree_item   *mti;
                    236:
                    237:        TAILQ_FOREACH(mti, mtl, entry) {
                    238:                mti->tagged = 0;
                    239:                mode_tree_clear_tagged(&mti->children);
                    240:        }
                    241: }
                    242:
1.46      nicm      243: void
1.1       nicm      244: mode_tree_up(struct mode_tree_data *mtd, int wrap)
                    245: {
                    246:        if (mtd->current == 0) {
                    247:                if (wrap) {
                    248:                        mtd->current = mtd->line_size - 1;
                    249:                        if (mtd->line_size >= mtd->height)
                    250:                                mtd->offset = mtd->line_size - mtd->height;
                    251:                }
                    252:        } else {
                    253:                mtd->current--;
                    254:                if (mtd->current < mtd->offset)
                    255:                        mtd->offset--;
                    256:        }
                    257: }
                    258:
                    259: void
                    260: mode_tree_down(struct mode_tree_data *mtd, int wrap)
                    261: {
                    262:        if (mtd->current == mtd->line_size - 1) {
                    263:                if (wrap) {
                    264:                        mtd->current = 0;
                    265:                        mtd->offset = 0;
                    266:                }
                    267:        } else {
                    268:                mtd->current++;
                    269:                if (mtd->current > mtd->offset + mtd->height - 1)
                    270:                        mtd->offset++;
                    271:        }
                    272: }
                    273:
                    274: void *
                    275: mode_tree_get_current(struct mode_tree_data *mtd)
                    276: {
                    277:        return (mtd->line_list[mtd->current].item->itemdata);
                    278: }
                    279:
1.46      nicm      280: const char *
                    281: mode_tree_get_current_name(struct mode_tree_data *mtd)
                    282: {
                    283:        return (mtd->line_list[mtd->current].item->name);
                    284: }
                    285:
1.19      nicm      286: void
                    287: mode_tree_expand_current(struct mode_tree_data *mtd)
                    288: {
                    289:        if (!mtd->line_list[mtd->current].item->expanded) {
                    290:                mtd->line_list[mtd->current].item->expanded = 1;
                    291:                mode_tree_build(mtd);
                    292:        }
                    293: }
                    294:
1.46      nicm      295: void
                    296: mode_tree_collapse_current(struct mode_tree_data *mtd)
                    297: {
                    298:        if (mtd->line_list[mtd->current].item->expanded) {
                    299:                mtd->line_list[mtd->current].item->expanded = 0;
                    300:                mode_tree_build(mtd);
                    301:        }
                    302: }
                    303:
1.41      nicm      304: static int
                    305: mode_tree_get_tag(struct mode_tree_data *mtd, uint64_t tag, u_int *found)
1.19      nicm      306: {
                    307:        u_int   i;
                    308:
                    309:        for (i = 0; i < mtd->line_size; i++) {
                    310:                if (mtd->line_list[i].item->tag == tag)
                    311:                        break;
                    312:        }
                    313:        if (i != mtd->line_size) {
1.41      nicm      314:                *found = i;
                    315:                return (1);
                    316:        }
                    317:        return (0);
                    318: }
                    319:
                    320: void
                    321: mode_tree_expand(struct mode_tree_data *mtd, uint64_t tag)
                    322: {
                    323:        u_int   found;
                    324:
                    325:        if (!mode_tree_get_tag(mtd, tag, &found))
                    326:            return;
                    327:        if (!mtd->line_list[found].item->expanded) {
                    328:                mtd->line_list[found].item->expanded = 1;
                    329:                mode_tree_build(mtd);
                    330:        }
                    331: }
                    332:
                    333: int
                    334: mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
                    335: {
                    336:        u_int   found;
                    337:
                    338:        if (mode_tree_get_tag(mtd, tag, &found)) {
                    339:                mtd->current = found;
1.19      nicm      340:                if (mtd->current > mtd->height - 1)
                    341:                        mtd->offset = mtd->current - mtd->height + 1;
                    342:                else
                    343:                        mtd->offset = 0;
1.41      nicm      344:                return (1);
1.19      nicm      345:        }
1.41      nicm      346:        mtd->current = 0;
                    347:        mtd->offset = 0;
                    348:        return (0);
1.19      nicm      349: }
                    350:
1.1       nicm      351: u_int
                    352: mode_tree_count_tagged(struct mode_tree_data *mtd)
                    353: {
                    354:        struct mode_tree_item   *mti;
                    355:        u_int                    i, tagged;
                    356:
                    357:        tagged = 0;
                    358:        for (i = 0; i < mtd->line_size; i++) {
                    359:                mti = mtd->line_list[i].item;
                    360:                if (mti->tagged)
                    361:                        tagged++;
                    362:        }
                    363:        return (tagged);
                    364: }
                    365:
                    366: void
1.17      nicm      367: mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb,
                    368:     struct client *c, key_code key, int current)
1.1       nicm      369: {
                    370:        struct mode_tree_item   *mti;
                    371:        u_int                    i;
                    372:        int                      fired;
                    373:
                    374:        fired = 0;
                    375:        for (i = 0; i < mtd->line_size; i++) {
                    376:                mti = mtd->line_list[i].item;
                    377:                if (mti->tagged) {
                    378:                        fired = 1;
1.14      nicm      379:                        cb(mtd->modedata, mti->itemdata, c, key);
1.1       nicm      380:                }
                    381:        }
                    382:        if (!fired && current) {
                    383:                mti = mtd->line_list[mtd->current].item;
1.14      nicm      384:                cb(mtd->modedata, mti->itemdata, c, key);
1.1       nicm      385:        }
                    386: }
                    387:
                    388: struct mode_tree_data *
1.5       nicm      389: mode_tree_start(struct window_pane *wp, struct args *args,
1.17      nicm      390:     mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
1.46      nicm      391:     mode_tree_search_cb searchcb, mode_tree_menu_cb menucb,
1.52    ! nicm      392:     mode_tree_height_cb heightcb, mode_tree_key_cb keycb, void *modedata,
1.33      nicm      393:     const struct menu_item *menu, const char **sort_list, u_int sort_size,
1.28      nicm      394:     struct screen **s)
1.1       nicm      395: {
                    396:        struct mode_tree_data   *mtd;
1.5       nicm      397:        const char              *sort;
                    398:        u_int                    i;
1.1       nicm      399:
                    400:        mtd = xcalloc(1, sizeof *mtd);
1.3       nicm      401:        mtd->references = 1;
                    402:
1.1       nicm      403:        mtd->wp = wp;
                    404:        mtd->modedata = modedata;
1.28      nicm      405:        mtd->menu = menu;
1.1       nicm      406:
                    407:        mtd->sort_list = sort_list;
                    408:        mtd->sort_size = sort_size;
1.5       nicm      409:
1.9       nicm      410:        mtd->preview = !args_has(args, 'N');
                    411:
1.5       nicm      412:        sort = args_get(args, 'O');
                    413:        if (sort != NULL) {
                    414:                for (i = 0; i < sort_size; i++) {
                    415:                        if (strcasecmp(sort, sort_list[i]) == 0)
1.36      nicm      416:                                mtd->sort_crit.field = i;
1.5       nicm      417:                }
                    418:        }
1.36      nicm      419:        mtd->sort_crit.reversed = args_has(args, 'r');
1.1       nicm      420:
1.6       nicm      421:        if (args_has(args, 'f'))
                    422:                mtd->filter = xstrdup(args_get(args, 'f'));
                    423:        else
                    424:                mtd->filter = NULL;
                    425:
1.1       nicm      426:        mtd->buildcb = buildcb;
                    427:        mtd->drawcb = drawcb;
1.3       nicm      428:        mtd->searchcb = searchcb;
1.28      nicm      429:        mtd->menucb = menucb;
1.46      nicm      430:        mtd->heightcb = heightcb;
1.52    ! nicm      431:        mtd->keycb = keycb;
1.1       nicm      432:
                    433:        TAILQ_INIT(&mtd->children);
                    434:
                    435:        *s = &mtd->screen;
                    436:        screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
                    437:        (*s)->mode &= ~MODE_CURSOR;
                    438:
                    439:        return (mtd);
                    440: }
                    441:
                    442: void
1.23      nicm      443: mode_tree_zoom(struct mode_tree_data *mtd, struct args *args)
                    444: {
                    445:        struct window_pane      *wp = mtd->wp;
                    446:
                    447:        if (args_has(args, 'Z')) {
                    448:                mtd->zoomed = (wp->window->flags & WINDOW_ZOOMED);
                    449:                if (!mtd->zoomed && window_zoom(wp) == 0)
                    450:                        server_redraw_window(wp->window);
                    451:        } else
                    452:                mtd->zoomed = -1;
                    453: }
                    454:
1.46      nicm      455: static void
                    456: mode_tree_set_height(struct mode_tree_data *mtd)
                    457: {
                    458:        struct screen   *s = &mtd->screen;
                    459:        u_int            height;
                    460:
                    461:        if (mtd->heightcb != NULL) {
                    462:                height = mtd->heightcb(mtd, screen_size_y(s));
                    463:                if (height < screen_size_y(s))
                    464:                    mtd->height = screen_size_y(s) - height;
                    465:        } else {
                    466:                mtd->height = (screen_size_y(s) / 3) * 2;
                    467:                if (mtd->height > mtd->line_size)
                    468:                        mtd->height = screen_size_y(s) / 2;
                    469:        }
                    470:        if (mtd->height < 10)
                    471:                mtd->height = screen_size_y(s);
                    472:        if (screen_size_y(s) - mtd->height < 2)
                    473:                mtd->height = screen_size_y(s);
                    474: }
                    475:
1.23      nicm      476: void
1.1       nicm      477: mode_tree_build(struct mode_tree_data *mtd)
                    478: {
                    479:        struct screen   *s = &mtd->screen;
                    480:        uint64_t         tag;
                    481:
                    482:        if (mtd->line_list != NULL)
                    483:                tag = mtd->line_list[mtd->current].item->tag;
                    484:        else
1.32      nicm      485:                tag = UINT64_MAX;
1.1       nicm      486:
                    487:        TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
                    488:        TAILQ_INIT(&mtd->children);
                    489:
1.36      nicm      490:        mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter);
1.21      nicm      491:        mtd->no_matches = TAILQ_EMPTY(&mtd->children);
                    492:        if (mtd->no_matches)
1.36      nicm      493:                mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, NULL);
1.1       nicm      494:
                    495:        mode_tree_free_items(&mtd->saved);
                    496:        TAILQ_INIT(&mtd->saved);
                    497:
                    498:        mode_tree_clear_lines(mtd);
                    499:        mode_tree_build_lines(mtd, &mtd->children, 0);
                    500:
1.32      nicm      501:        if (tag == UINT64_MAX)
                    502:                tag = mtd->line_list[mtd->current].item->tag;
1.3       nicm      503:        mode_tree_set_current(mtd, tag);
1.1       nicm      504:
                    505:        mtd->width = screen_size_x(s);
1.46      nicm      506:        if (mtd->preview)
                    507:                mode_tree_set_height(mtd);
                    508:        else
1.1       nicm      509:                mtd->height = screen_size_y(s);
1.11      nicm      510:        mode_tree_check_selected(mtd);
1.1       nicm      511: }
                    512:
1.3       nicm      513: static void
                    514: mode_tree_remove_ref(struct mode_tree_data *mtd)
                    515: {
                    516:        if (--mtd->references == 0)
                    517:                free(mtd);
                    518: }
                    519:
1.1       nicm      520: void
                    521: mode_tree_free(struct mode_tree_data *mtd)
                    522: {
1.23      nicm      523:        struct window_pane      *wp = mtd->wp;
                    524:
                    525:        if (mtd->zoomed == 0)
                    526:                server_unzoom_window(wp->window);
                    527:
1.1       nicm      528:        mode_tree_free_items(&mtd->children);
                    529:        mode_tree_clear_lines(mtd);
                    530:        screen_free(&mtd->screen);
1.3       nicm      531:
1.6       nicm      532:        free(mtd->search);
                    533:        free(mtd->filter);
                    534:
1.3       nicm      535:        mtd->dead = 1;
                    536:        mode_tree_remove_ref(mtd);
1.1       nicm      537: }
                    538:
                    539: void
                    540: mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
                    541: {
                    542:        struct screen   *s = &mtd->screen;
                    543:
                    544:        screen_resize(s, sx, sy, 0);
                    545:
                    546:        mode_tree_build(mtd);
                    547:        mode_tree_draw(mtd);
                    548:
                    549:        mtd->wp->flags |= PANE_REDRAW;
                    550: }
                    551:
                    552: struct mode_tree_item *
                    553: mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
                    554:     void *itemdata, uint64_t tag, const char *name, const char *text,
                    555:     int expanded)
                    556: {
                    557:        struct mode_tree_item   *mti, *saved;
                    558:
                    559:        log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
1.46      nicm      560:            name, (text == NULL ? "" : text));
1.1       nicm      561:
                    562:        mti = xcalloc(1, sizeof *mti);
                    563:        mti->parent = parent;
                    564:        mti->itemdata = itemdata;
                    565:
                    566:        mti->tag = tag;
                    567:        mti->name = xstrdup(name);
1.46      nicm      568:        if (text != NULL)
                    569:                mti->text = xstrdup(text);
1.1       nicm      570:
                    571:        saved = mode_tree_find_item(&mtd->saved, tag);
                    572:        if (saved != NULL) {
1.34      nicm      573:                if (parent == NULL || parent->expanded)
1.1       nicm      574:                        mti->tagged = saved->tagged;
                    575:                mti->expanded = saved->expanded;
                    576:        } else if (expanded == -1)
                    577:                mti->expanded = 1;
                    578:        else
                    579:                mti->expanded = expanded;
                    580:
                    581:        TAILQ_INIT(&mti->children);
                    582:
                    583:        if (parent != NULL)
                    584:                TAILQ_INSERT_TAIL(&parent->children, mti, entry);
                    585:        else
                    586:                TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
                    587:
                    588:        return (mti);
                    589: }
                    590:
                    591: void
1.46      nicm      592: mode_tree_draw_as_parent(struct mode_tree_item *mti)
                    593: {
                    594:        mti->draw_as_parent = 1;
                    595: }
                    596:
                    597: void
1.50      nicm      598: mode_tree_no_tag(struct mode_tree_item *mti)
                    599: {
                    600:        mti->no_tag = 1;
                    601: }
                    602:
                    603: void
1.1       nicm      604: mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
                    605: {
                    606:        struct mode_tree_item   *parent = mti->parent;
                    607:
                    608:        if (parent != NULL)
                    609:                TAILQ_REMOVE(&parent->children, mti, entry);
                    610:        else
                    611:                TAILQ_REMOVE(&mtd->children, mti, entry);
                    612:        mode_tree_free_item(mti);
                    613: }
                    614:
                    615: void
                    616: mode_tree_draw(struct mode_tree_data *mtd)
                    617: {
                    618:        struct window_pane      *wp = mtd->wp;
1.17      nicm      619:        struct screen           *s = &mtd->screen;
1.1       nicm      620:        struct mode_tree_line   *line;
                    621:        struct mode_tree_item   *mti;
                    622:        struct options          *oo = wp->window->options;
                    623:        struct screen_write_ctx  ctx;
                    624:        struct grid_cell         gc0, gc;
1.27      nicm      625:        u_int                    w, h, i, j, sy, box_x, box_y, width;
1.52    ! nicm      626:        char                    *text, *start, *key;
1.1       nicm      627:        const char              *tag, *symbol;
1.21      nicm      628:        size_t                   size, n;
1.52    ! nicm      629:        int                      keylen, pad;
1.1       nicm      630:
                    631:        if (mtd->line_size == 0)
                    632:                return;
                    633:
                    634:        memcpy(&gc0, &grid_default_cell, sizeof gc0);
                    635:        memcpy(&gc, &grid_default_cell, sizeof gc);
1.42      nicm      636:        style_apply(&gc, oo, "mode-style", NULL);
1.1       nicm      637:
                    638:        w = mtd->width;
                    639:        h = mtd->height;
                    640:
1.43      nicm      641:        screen_write_start(&ctx, s);
1.1       nicm      642:        screen_write_clearscreen(&ctx, 8);
                    643:
1.52    ! nicm      644:        keylen = 0;
        !           645:        for (i = 0; i < mtd->line_size; i++) {
        !           646:                mti = mtd->line_list[i].item;
        !           647:                if (mti->key == KEYC_NONE)
        !           648:                        continue;
        !           649:                if ((int)mti->keylen + 3 > keylen)
        !           650:                        keylen = mti->keylen + 3;
        !           651:        }
1.1       nicm      652:
                    653:        for (i = 0; i < mtd->line_size; i++) {
                    654:                if (i < mtd->offset)
                    655:                        continue;
                    656:                if (i > mtd->offset + h - 1)
                    657:                        break;
                    658:                line = &mtd->line_list[i];
                    659:                mti = line->item;
                    660:
1.26      nicm      661:                screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
1.1       nicm      662:
1.52    ! nicm      663:                pad = keylen - 2 - mti->keylen;
        !           664:                if (mti->key != KEYC_NONE)
        !           665:                        xasprintf(&key, "(%s)%*s", mti->keystr, pad, "");
1.1       nicm      666:                else
1.52    ! nicm      667:                        key = xstrdup("");
1.1       nicm      668:
                    669:                if (line->flat)
                    670:                        symbol = "";
                    671:                else if (TAILQ_EMPTY(&mti->children))
                    672:                        symbol = "  ";
                    673:                else if (mti->expanded)
                    674:                        symbol = "- ";
                    675:                else
                    676:                        symbol = "+ ";
                    677:
                    678:                if (line->depth == 0)
                    679:                        start = xstrdup(symbol);
                    680:                else {
                    681:                        size = (4 * line->depth) + 32;
                    682:
                    683:                        start = xcalloc(1, size);
                    684:                        for (j = 1; j < line->depth; j++) {
                    685:                                if (mti->parent != NULL &&
                    686:                                    mtd->line_list[mti->parent->line].last)
                    687:                                        strlcat(start, "    ", size);
                    688:                                else
                    689:                                        strlcat(start, "\001x\001   ", size);
                    690:                        }
                    691:                        if (line->last)
                    692:                                strlcat(start, "\001mq\001> ", size);
                    693:                        else
                    694:                                strlcat(start, "\001tq\001> ", size);
                    695:                        strlcat(start, symbol, size);
                    696:                }
                    697:
                    698:                if (mti->tagged)
                    699:                        tag = "*";
                    700:                else
                    701:                        tag = "";
1.46      nicm      702:                xasprintf(&text, "%-*s%s%s%s%s", keylen, key, start, mti->name,
                    703:                    tag, (mti->text != NULL) ? ": " : "" );
1.27      nicm      704:                width = utf8_cstrwidth(text);
1.37      nicm      705:                if (width > w)
                    706:                        width = w;
1.1       nicm      707:                free(start);
                    708:
                    709:                if (mti->tagged) {
                    710:                        gc.attr ^= GRID_ATTR_BRIGHT;
                    711:                        gc0.attr ^= GRID_ATTR_BRIGHT;
                    712:                }
                    713:
                    714:                if (i != mtd->current) {
                    715:                        screen_write_clearendofline(&ctx, 8);
1.37      nicm      716:                        screen_write_nputs(&ctx, w, &gc0, "%s", text);
1.46      nicm      717:                        if (mti->text != NULL) {
                    718:                                format_draw(&ctx, &gc0, w - width, mti->text,
                    719:                                    NULL);
                    720:                        }
1.13      nicm      721:                } else {
                    722:                        screen_write_clearendofline(&ctx, gc.bg);
1.37      nicm      723:                        screen_write_nputs(&ctx, w, &gc, "%s", text);
1.46      nicm      724:                        if (mti->text != NULL) {
                    725:                                format_draw(&ctx, &gc, w - width, mti->text,
                    726:                                    NULL);
                    727:                        }
1.13      nicm      728:                }
1.1       nicm      729:                free(text);
1.52    ! nicm      730:                free(key);
1.1       nicm      731:
                    732:                if (mti->tagged) {
                    733:                        gc.attr ^= GRID_ATTR_BRIGHT;
                    734:                        gc0.attr ^= GRID_ATTR_BRIGHT;
                    735:                }
                    736:        }
                    737:
                    738:        sy = screen_size_y(s);
1.9       nicm      739:        if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
1.1       nicm      740:                screen_write_stop(&ctx);
                    741:                return;
                    742:        }
                    743:
                    744:        line = &mtd->line_list[mtd->current];
                    745:        mti = line->item;
1.46      nicm      746:        if (mti->draw_as_parent)
                    747:                mti = mti->parent;
1.1       nicm      748:
1.26      nicm      749:        screen_write_cursormove(&ctx, 0, h, 0);
1.1       nicm      750:        screen_write_box(&ctx, w, sy - h);
                    751:
1.46      nicm      752:        if (mtd->sort_list != NULL) {
                    753:                xasprintf(&text, " %s (sort: %s%s)", mti->name,
                    754:                    mtd->sort_list[mtd->sort_crit.field],
                    755:                    mtd->sort_crit.reversed ? ", reversed" : "");
                    756:        } else
                    757:                xasprintf(&text, " %s", mti->name);
1.1       nicm      758:        if (w - 2 >= strlen(text)) {
1.26      nicm      759:                screen_write_cursormove(&ctx, 1, h, 0);
1.1       nicm      760:                screen_write_puts(&ctx, &gc0, "%s", text);
1.21      nicm      761:
                    762:                if (mtd->no_matches)
                    763:                        n = (sizeof "no matches") - 1;
                    764:                else
                    765:                        n = (sizeof "active") - 1;
                    766:                if (mtd->filter != NULL && w - 2 >= strlen(text) + 10 + n + 2) {
                    767:                        screen_write_puts(&ctx, &gc0, " (filter: ");
                    768:                        if (mtd->no_matches)
                    769:                                screen_write_puts(&ctx, &gc, "no matches");
                    770:                        else
                    771:                                screen_write_puts(&ctx, &gc0, "active");
                    772:                        screen_write_puts(&ctx, &gc0, ") ");
1.46      nicm      773:                } else
                    774:                        screen_write_puts(&ctx, &gc0, " ");
1.1       nicm      775:        }
                    776:        free(text);
                    777:
                    778:        box_x = w - 4;
                    779:        box_y = sy - h - 2;
                    780:
1.17      nicm      781:        if (box_x != 0 && box_y != 0) {
1.26      nicm      782:                screen_write_cursormove(&ctx, 2, h + 1, 0);
1.17      nicm      783:                mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y);
1.1       nicm      784:        }
                    785:
                    786:        screen_write_stop(&ctx);
                    787: }
                    788:
1.3       nicm      789: static struct mode_tree_item *
                    790: mode_tree_search_for(struct mode_tree_data *mtd)
                    791: {
                    792:        struct mode_tree_item   *mti, *last, *next;
                    793:
1.6       nicm      794:        if (mtd->search == NULL)
1.3       nicm      795:                return (NULL);
                    796:
                    797:        mti = last = mtd->line_list[mtd->current].item;
                    798:        for (;;) {
                    799:                if (!TAILQ_EMPTY(&mti->children))
                    800:                        mti = TAILQ_FIRST(&mti->children);
                    801:                else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
                    802:                        mti = next;
                    803:                else {
                    804:                        for (;;) {
                    805:                                mti = mti->parent;
                    806:                                if (mti == NULL)
                    807:                                        break;
                    808:                                if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
                    809:                                        mti = next;
                    810:                                        break;
                    811:                                }
                    812:                        }
                    813:                }
                    814:                if (mti == NULL)
                    815:                        mti = TAILQ_FIRST(&mtd->children);
                    816:                if (mti == last)
                    817:                        break;
                    818:
                    819:                if (mtd->searchcb == NULL) {
1.6       nicm      820:                        if (strstr(mti->name, mtd->search) != NULL)
1.3       nicm      821:                                return (mti);
                    822:                        continue;
                    823:                }
1.6       nicm      824:                if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
1.4       nicm      825:                        return (mti);
1.3       nicm      826:        }
                    827:        return (NULL);
                    828: }
                    829:
                    830: static void
                    831: mode_tree_search_set(struct mode_tree_data *mtd)
                    832: {
                    833:        struct mode_tree_item   *mti, *loop;
                    834:        uint64_t                 tag;
                    835:
                    836:        mti = mode_tree_search_for(mtd);
                    837:        if (mti == NULL)
                    838:                return;
                    839:        tag = mti->tag;
                    840:
                    841:        loop = mti->parent;
                    842:        while (loop != NULL) {
                    843:                loop->expanded = 1;
                    844:                loop = loop->parent;
                    845:        }
1.6       nicm      846:
1.3       nicm      847:        mode_tree_build(mtd);
                    848:        mode_tree_set_current(mtd, tag);
                    849:        mode_tree_draw(mtd);
1.6       nicm      850:        mtd->wp->flags |= PANE_REDRAW;
1.3       nicm      851: }
                    852:
                    853: static int
                    854: mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
                    855:     __unused int done)
                    856: {
                    857:        struct mode_tree_data   *mtd = data;
                    858:
                    859:        if (mtd->dead)
                    860:                return (0);
                    861:
1.6       nicm      862:        free(mtd->search);
                    863:        if (s == NULL || *s == '\0') {
                    864:                mtd->search = NULL;
1.3       nicm      865:                return (0);
                    866:        }
1.6       nicm      867:        mtd->search = xstrdup(s);
1.3       nicm      868:        mode_tree_search_set(mtd);
                    869:
                    870:        return (0);
                    871: }
                    872:
                    873: static void
                    874: mode_tree_search_free(void *data)
                    875: {
                    876:        mode_tree_remove_ref(data);
                    877: }
                    878:
1.6       nicm      879: static int
                    880: mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
                    881:     __unused int done)
                    882: {
                    883:        struct mode_tree_data   *mtd = data;
                    884:
                    885:        if (mtd->dead)
                    886:                return (0);
                    887:
                    888:        if (mtd->filter != NULL)
                    889:                free(mtd->filter);
                    890:        if (s == NULL || *s == '\0')
                    891:                mtd->filter = NULL;
                    892:        else
                    893:                mtd->filter = xstrdup(s);
                    894:
                    895:        mode_tree_build(mtd);
                    896:        mode_tree_draw(mtd);
                    897:        mtd->wp->flags |= PANE_REDRAW;
                    898:
                    899:        return (0);
                    900: }
                    901:
                    902: static void
                    903: mode_tree_filter_free(void *data)
                    904: {
                    905:        mode_tree_remove_ref(data);
                    906: }
                    907:
1.28      nicm      908: static void
                    909: mode_tree_menu_callback(__unused struct menu *menu, __unused u_int idx,
                    910:     key_code key, void *data)
                    911: {
                    912:        struct mode_tree_menu           *mtm = data;
                    913:        struct mode_tree_data           *mtd = mtm->data;
                    914:        struct mode_tree_item           *mti;
                    915:
                    916:        if (mtd->dead || key == KEYC_NONE)
                    917:                goto out;
                    918:
                    919:        if (mtm->line >= mtd->line_size)
                    920:                goto out;
                    921:        mti = mtd->line_list[mtm->line].item;
                    922:        if (mti->itemdata != mtm->itemdata)
                    923:                goto out;
                    924:        mtd->current = mtm->line;
                    925:        mtd->menucb (mtd->modedata, mtm->c, key);
                    926:
                    927: out:
                    928:        mode_tree_remove_ref(mtd);
                    929:        free(mtm);
                    930: }
                    931:
                    932: static void
                    933: mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
                    934:     u_int y, int outside)
                    935: {
                    936:        struct mode_tree_item   *mti;
                    937:        struct menu             *menu;
1.33      nicm      938:        const struct menu_item  *items;
1.28      nicm      939:        struct mode_tree_menu   *mtm;
                    940:        char                    *title;
                    941:        u_int                    line;
                    942:
                    943:        if (mtd->offset + y > mtd->line_size - 1)
                    944:                line = mtd->current;
                    945:        else
                    946:                line = mtd->offset + y;
1.29      nicm      947:        mti = mtd->line_list[line].item;
1.28      nicm      948:
                    949:        if (!outside) {
1.33      nicm      950:                items = mtd->menu;
1.28      nicm      951:                xasprintf(&title, "#[align=centre]%s", mti->name);
                    952:        } else {
1.33      nicm      953:                items = mode_tree_menu_items;
1.28      nicm      954:                title = xstrdup("");
                    955:        }
1.33      nicm      956:        menu = menu_create(title);
                    957:        menu_add_items(menu, items, NULL, NULL, NULL);
1.28      nicm      958:        free(title);
                    959:
                    960:        mtm = xmalloc(sizeof *mtm);
                    961:        mtm->data = mtd;
                    962:        mtm->c = c;
                    963:        mtm->line = line;
                    964:        mtm->itemdata = mti->itemdata;
                    965:        mtd->references++;
                    966:
1.38      nicm      967:        if (x >= (menu->width + 4) / 2)
                    968:                x -= (menu->width + 4) / 2;
                    969:        else
                    970:                x = 0;
1.28      nicm      971:        if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback,
                    972:            mtm) != 0)
                    973:                menu_free(menu);
                    974: }
                    975:
1.1       nicm      976: int
1.3       nicm      977: mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
1.19      nicm      978:     struct mouse_event *m, u_int *xp, u_int *yp)
1.1       nicm      979: {
                    980:        struct mode_tree_line   *line;
1.44      nicm      981:        struct mode_tree_item   *current, *parent, *mti;
1.1       nicm      982:        u_int                    i, x, y;
                    983:        int                      choice;
                    984:
1.28      nicm      985:        if (KEYC_IS_MOUSE(*key) && m != NULL) {
1.1       nicm      986:                if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
                    987:                        *key = KEYC_NONE;
                    988:                        return (0);
                    989:                }
1.19      nicm      990:                if (xp != NULL)
                    991:                        *xp = x;
                    992:                if (yp != NULL)
                    993:                        *yp = y;
1.1       nicm      994:                if (x > mtd->width || y > mtd->height) {
1.28      nicm      995:                        if (*key == KEYC_MOUSEDOWN3_PANE)
                    996:                                mode_tree_display_menu(mtd, c, x, y, 1);
1.20      nicm      997:                        if (!mtd->preview)
1.19      nicm      998:                                *key = KEYC_NONE;
1.1       nicm      999:                        return (0);
                   1000:                }
                   1001:                if (mtd->offset + y < mtd->line_size) {
1.18      nicm     1002:                        if (*key == KEYC_MOUSEDOWN1_PANE ||
1.28      nicm     1003:                            *key == KEYC_MOUSEDOWN3_PANE ||
1.18      nicm     1004:                            *key == KEYC_DOUBLECLICK1_PANE)
                   1005:                                mtd->current = mtd->offset + y;
                   1006:                        if (*key == KEYC_DOUBLECLICK1_PANE)
                   1007:                                *key = '\r';
1.28      nicm     1008:                        else {
                   1009:                                if (*key == KEYC_MOUSEDOWN3_PANE)
                   1010:                                        mode_tree_display_menu(mtd, c, x, y, 0);
1.20      nicm     1011:                                *key = KEYC_NONE;
1.28      nicm     1012:                        }
                   1013:                } else {
                   1014:                        if (*key == KEYC_MOUSEDOWN3_PANE)
                   1015:                                mode_tree_display_menu(mtd, c, x, y, 0);
1.20      nicm     1016:                        *key = KEYC_NONE;
1.28      nicm     1017:                }
1.20      nicm     1018:                return (0);
1.1       nicm     1019:        }
                   1020:
                   1021:        line = &mtd->line_list[mtd->current];
                   1022:        current = line->item;
                   1023:
                   1024:        choice = -1;
1.52    ! nicm     1025:        for (i = 0; i < mtd->line_size; i++) {
        !          1026:                if (*key == mtd->line_list[i].item->key) {
        !          1027:                        choice = i;
        !          1028:                        break;
        !          1029:                }
1.1       nicm     1030:        }
                   1031:        if (choice != -1) {
                   1032:                if ((u_int)choice > mtd->line_size - 1) {
                   1033:                        *key = KEYC_NONE;
                   1034:                        return (0);
                   1035:                }
                   1036:                mtd->current = choice;
                   1037:                *key = '\r';
                   1038:                return (0);
                   1039:        }
                   1040:
                   1041:        switch (*key) {
                   1042:        case 'q':
                   1043:        case '\033': /* Escape */
1.22      nicm     1044:        case '\007': /* C-g */
1.1       nicm     1045:                return (1);
                   1046:        case KEYC_UP:
                   1047:        case 'k':
                   1048:        case KEYC_WHEELUP_PANE:
1.12      nicm     1049:        case '\020': /* C-p */
1.1       nicm     1050:                mode_tree_up(mtd, 1);
                   1051:                break;
                   1052:        case KEYC_DOWN:
                   1053:        case 'j':
                   1054:        case KEYC_WHEELDOWN_PANE:
1.12      nicm     1055:        case '\016': /* C-n */
1.1       nicm     1056:                mode_tree_down(mtd, 1);
                   1057:                break;
1.35      nicm     1058:        case 'g':
1.1       nicm     1059:        case KEYC_PPAGE:
                   1060:        case '\002': /* C-b */
                   1061:                for (i = 0; i < mtd->height; i++) {
                   1062:                        if (mtd->current == 0)
                   1063:                                break;
                   1064:                        mode_tree_up(mtd, 1);
                   1065:                }
                   1066:                break;
1.35      nicm     1067:        case 'G':
1.1       nicm     1068:        case KEYC_NPAGE:
                   1069:        case '\006': /* C-f */
                   1070:                for (i = 0; i < mtd->height; i++) {
                   1071:                        if (mtd->current == mtd->line_size - 1)
                   1072:                                break;
                   1073:                        mode_tree_down(mtd, 1);
                   1074:                }
                   1075:                break;
                   1076:        case KEYC_HOME:
                   1077:                mtd->current = 0;
                   1078:                mtd->offset = 0;
                   1079:                break;
                   1080:        case KEYC_END:
                   1081:                mtd->current = mtd->line_size - 1;
                   1082:                if (mtd->current > mtd->height - 1)
1.11      nicm     1083:                        mtd->offset = mtd->current - mtd->height + 1;
1.1       nicm     1084:                else
                   1085:                        mtd->offset = 0;
                   1086:                break;
                   1087:        case 't':
                   1088:                /*
                   1089:                 * Do not allow parents and children to both be tagged: untag
                   1090:                 * all parents and children of current.
                   1091:                 */
1.50      nicm     1092:                if (current->no_tag)
                   1093:                        break;
1.1       nicm     1094:                if (!current->tagged) {
                   1095:                        parent = current->parent;
                   1096:                        while (parent != NULL) {
                   1097:                                parent->tagged = 0;
                   1098:                                parent = parent->parent;
                   1099:                        }
                   1100:                        mode_tree_clear_tagged(&current->children);
                   1101:                        current->tagged = 1;
                   1102:                } else
                   1103:                        current->tagged = 0;
1.28      nicm     1104:                if (m != NULL)
                   1105:                        mode_tree_down(mtd, 0);
1.1       nicm     1106:                break;
                   1107:        case 'T':
                   1108:                for (i = 0; i < mtd->line_size; i++)
                   1109:                        mtd->line_list[i].item->tagged = 0;
                   1110:                break;
                   1111:        case '\024': /* C-t */
                   1112:                for (i = 0; i < mtd->line_size; i++) {
1.50      nicm     1113:                        if ((mtd->line_list[i].item->parent == NULL &&
                   1114:                            !mtd->line_list[i].item->no_tag) ||
                   1115:                            (mtd->line_list[i].item->parent != NULL &&
                   1116:                            mtd->line_list[i].item->parent->no_tag))
1.1       nicm     1117:                                mtd->line_list[i].item->tagged = 1;
                   1118:                        else
                   1119:                                mtd->line_list[i].item->tagged = 0;
                   1120:                }
                   1121:                break;
                   1122:        case 'O':
1.36      nicm     1123:                mtd->sort_crit.field++;
1.46      nicm     1124:                if (mtd->sort_crit.field >= mtd->sort_size)
1.36      nicm     1125:                        mtd->sort_crit.field = 0;
                   1126:                mode_tree_build(mtd);
                   1127:                break;
                   1128:        case 'r':
                   1129:                mtd->sort_crit.reversed = !mtd->sort_crit.reversed;
1.1       nicm     1130:                mode_tree_build(mtd);
                   1131:                break;
                   1132:        case KEYC_LEFT:
1.15      nicm     1133:        case 'h':
1.1       nicm     1134:        case '-':
                   1135:                if (line->flat || !current->expanded)
                   1136:                        current = current->parent;
                   1137:                if (current == NULL)
                   1138:                        mode_tree_up(mtd, 0);
                   1139:                else {
                   1140:                        current->expanded = 0;
                   1141:                        mtd->current = current->line;
                   1142:                        mode_tree_build(mtd);
                   1143:                }
                   1144:                break;
                   1145:        case KEYC_RIGHT:
1.15      nicm     1146:        case 'l':
1.1       nicm     1147:        case '+':
                   1148:                if (line->flat || current->expanded)
                   1149:                        mode_tree_down(mtd, 0);
                   1150:                else if (!line->flat) {
                   1151:                        current->expanded = 1;
                   1152:                        mode_tree_build(mtd);
                   1153:                }
1.44      nicm     1154:                break;
1.48      nicm     1155:        case '-'|KEYC_META:
1.44      nicm     1156:                TAILQ_FOREACH(mti, &mtd->children, entry)
                   1157:                        mti->expanded = 0;
                   1158:                mode_tree_build(mtd);
                   1159:                break;
1.48      nicm     1160:        case '+'|KEYC_META:
1.44      nicm     1161:                TAILQ_FOREACH(mti, &mtd->children, entry)
                   1162:                        mti->expanded = 1;
                   1163:                mode_tree_build(mtd);
1.3       nicm     1164:                break;
1.35      nicm     1165:        case '?':
                   1166:        case '/':
1.3       nicm     1167:        case '\023': /* C-s */
                   1168:                mtd->references++;
1.47      nicm     1169:                status_prompt_set(c, NULL, "(search) ", "",
1.3       nicm     1170:                    mode_tree_search_callback, mode_tree_search_free, mtd,
                   1171:                    PROMPT_NOFORMAT);
                   1172:                break;
                   1173:        case 'n':
                   1174:                mode_tree_search_set(mtd);
1.6       nicm     1175:                break;
                   1176:        case 'f':
                   1177:                mtd->references++;
1.47      nicm     1178:                status_prompt_set(c, NULL, "(filter) ", mtd->filter,
1.6       nicm     1179:                    mode_tree_filter_callback, mode_tree_filter_free, mtd,
                   1180:                    PROMPT_NOFORMAT);
1.9       nicm     1181:                break;
                   1182:        case 'v':
                   1183:                mtd->preview = !mtd->preview;
                   1184:                mode_tree_build(mtd);
1.11      nicm     1185:                if (mtd->preview)
                   1186:                        mode_tree_check_selected(mtd);
1.1       nicm     1187:                break;
                   1188:        }
                   1189:        return (0);
                   1190: }
                   1191:
                   1192: void
                   1193: mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
                   1194:     const char *template, const char *name)
                   1195: {
1.40      nicm     1196:        struct cmdq_state       *state;
                   1197:        char                    *command, *error;
                   1198:        enum cmd_parse_status    status;
1.1       nicm     1199:
                   1200:        command = cmd_template_replace(template, name, 1);
1.40      nicm     1201:        if (command != NULL && *command != '\0') {
                   1202:                state = cmdq_new_state(fs, NULL, 0);
                   1203:                status = cmd_parse_and_append(command, NULL, c, state, &error);
                   1204:                if (status == CMD_PARSE_ERROR) {
                   1205:                        if (c != NULL) {
                   1206:                                *error = toupper((u_char)*error);
1.51      nicm     1207:                                status_message_set(c, -1, 1, "%s", error);
1.40      nicm     1208:                        }
                   1209:                        free(error);
1.1       nicm     1210:                }
1.40      nicm     1211:                cmdq_free_state(state);
1.1       nicm     1212:        }
                   1213:        free(command);
                   1214: }