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

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