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

Annotation of src/usr.bin/tmux/window-tree.c, Revision 1.27

1.27    ! nicm        1: /* $OpenBSD: window-tree.c,v 1.26 2017/11/02 22:00:42 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 <stdlib.h>
                     22: #include <string.h>
                     23:
                     24: #include "tmux.h"
                     25:
                     26: static struct screen   *window_tree_init(struct window_pane *,
                     27:                             struct cmd_find_state *, struct args *);
                     28: static void             window_tree_free(struct window_pane *);
                     29: static void             window_tree_resize(struct window_pane *, u_int, u_int);
                     30: static void             window_tree_key(struct window_pane *,
                     31:                             struct client *, struct session *, key_code,
                     32:                             struct mouse_event *);
                     33:
                     34: #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'"
                     35:
1.15      nicm       36: #define WINDOW_TREE_DEFAULT_FORMAT \
                     37:        "#{?pane_format," \
                     38:                "#{pane_current_command} \"#{pane_title}\"" \
                     39:        "," \
                     40:                "#{?window_format," \
                     41:                        "#{window_name}#{window_flags} " \
                     42:                        "(#{window_panes} panes)" \
                     43:                        "#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \
                     44:                "," \
                     45:                        "#{session_windows} windows" \
1.23      nicm       46:                        "#{?session_grouped, " \
1.25      nicm       47:                                "(group #{session_group}: " \
                     48:                                "#{session_group_list})," \
1.23      nicm       49:                        "}" \
1.15      nicm       50:                        "#{?session_attached, (attached),}" \
                     51:                "}" \
                     52:        "}"
                     53:
1.1       nicm       54: const struct window_mode window_tree_mode = {
                     55:        .name = "tree-mode",
                     56:
                     57:        .init = window_tree_init,
                     58:        .free = window_tree_free,
                     59:        .resize = window_tree_resize,
                     60:        .key = window_tree_key,
                     61: };
                     62:
                     63: enum window_tree_sort_type {
                     64:        WINDOW_TREE_BY_INDEX,
                     65:        WINDOW_TREE_BY_NAME,
                     66:        WINDOW_TREE_BY_TIME,
                     67: };
                     68: static const char *window_tree_sort_list[] = {
                     69:        "index",
                     70:        "name",
                     71:        "time"
                     72: };
                     73:
                     74: enum window_tree_type {
                     75:        WINDOW_TREE_NONE,
                     76:        WINDOW_TREE_SESSION,
                     77:        WINDOW_TREE_WINDOW,
                     78:        WINDOW_TREE_PANE,
                     79: };
                     80:
                     81: struct window_tree_itemdata {
                     82:        enum window_tree_type   type;
                     83:        int                     session;
                     84:        int                     winlink;
                     85:        int                     pane;
                     86: };
                     87:
                     88: struct window_tree_modedata {
                     89:        struct window_pane               *wp;
                     90:        int                               dead;
                     91:        int                               references;
                     92:
                     93:        struct mode_tree_data            *data;
1.15      nicm       94:        char                             *format;
1.1       nicm       95:        char                             *command;
1.23      nicm       96:        int                               squash_groups;
1.1       nicm       97:
                     98:        struct window_tree_itemdata     **item_list;
                     99:        u_int                             item_size;
                    100:
                    101:        const char                       *entered;
                    102:
                    103:        struct cmd_find_state             fs;
                    104:        enum window_tree_type             type;
1.11      nicm      105:
                    106:        int                               offset;
1.27    ! nicm      107:
        !           108:        int                               left;
        !           109:        int                               right;
        !           110:        u_int                             start;
        !           111:        u_int                             end;
        !           112:        u_int                             each;
1.1       nicm      113: };
                    114:
                    115: static void
                    116: window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
                    117:     struct winlink **wlp, struct window_pane **wp)
                    118: {
                    119:        *wp = NULL;
                    120:        *wlp = NULL;
                    121:        *sp = session_find_by_id(item->session);
                    122:        if (*sp == NULL)
                    123:                return;
                    124:        if (item->type == WINDOW_TREE_SESSION) {
                    125:                *wlp = (*sp)->curw;
                    126:                *wp = (*wlp)->window->active;
                    127:                return;
                    128:        }
                    129:
                    130:        *wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
                    131:        if (*wlp == NULL) {
                    132:                *sp = NULL;
                    133:                return;
                    134:        }
                    135:        if (item->type == WINDOW_TREE_WINDOW) {
                    136:                *wp = (*wlp)->window->active;
                    137:                return;
                    138:        }
                    139:
                    140:        *wp = window_pane_find_by_id(item->pane);
                    141:        if (!window_has_pane((*wlp)->window, *wp))
                    142:                *wp = NULL;
                    143:        if (*wp == NULL) {
                    144:                *sp = NULL;
                    145:                *wlp = NULL;
                    146:                return;
                    147:        }
                    148: }
                    149:
                    150: static struct window_tree_itemdata *
                    151: window_tree_add_item(struct window_tree_modedata *data)
                    152: {
                    153:        struct window_tree_itemdata     *item;
                    154:
                    155:        data->item_list = xreallocarray(data->item_list, data->item_size + 1,
                    156:            sizeof *data->item_list);
                    157:        item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
                    158:        return (item);
                    159: }
                    160:
                    161: static void
                    162: window_tree_free_item(struct window_tree_itemdata *item)
                    163: {
                    164:        free(item);
                    165: }
                    166:
                    167: static int
                    168: window_tree_cmp_session_name(const void *a0, const void *b0)
                    169: {
                    170:        const struct session *const *a = a0;
                    171:        const struct session *const *b = b0;
                    172:
                    173:        return (strcmp((*a)->name, (*b)->name));
                    174: }
                    175:
                    176: static int
                    177: window_tree_cmp_session_time(const void *a0, const void *b0)
                    178: {
                    179:        const struct session *const *a = a0;
                    180:        const struct session *const *b = b0;
                    181:
                    182:        if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >))
                    183:                return (-1);
                    184:        if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <))
                    185:                return (1);
                    186:        return (strcmp((*a)->name, (*b)->name));
                    187: }
                    188:
                    189: static int
                    190: window_tree_cmp_window_name(const void *a0, const void *b0)
                    191: {
                    192:        const struct winlink *const *a = a0;
                    193:        const struct winlink *const *b = b0;
                    194:
                    195:        return (strcmp((*a)->window->name, (*b)->window->name));
                    196: }
                    197:
                    198: static int
                    199: window_tree_cmp_window_time(const void *a0, const void *b0)
                    200: {
                    201:        const struct winlink *const *a = a0;
                    202:        const struct winlink *const *b = b0;
                    203:
                    204:        if (timercmp(&(*a)->window->activity_time,
                    205:            &(*b)->window->activity_time, >))
                    206:                return (-1);
                    207:        if (timercmp(&(*a)->window->activity_time,
                    208:            &(*b)->window->activity_time, <))
                    209:                return (1);
                    210:        return (strcmp((*a)->window->name, (*b)->window->name));
                    211: }
                    212:
                    213: static int
                    214: window_tree_cmp_pane_time(const void *a0, const void *b0)
                    215: {
                    216:        const struct window_pane *const *a = a0;
                    217:        const struct window_pane *const *b = b0;
                    218:
                    219:        if ((*a)->active_point < (*b)->active_point)
                    220:                return (-1);
                    221:        if ((*a)->active_point > (*b)->active_point)
                    222:                return (1);
                    223:        return (0);
                    224: }
                    225:
                    226: static void
                    227: window_tree_build_pane(struct session *s, struct winlink *wl,
                    228:     struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
                    229: {
                    230:        struct window_tree_modedata     *data = modedata;
                    231:        struct window_tree_itemdata     *item;
                    232:        char                            *name, *text;
                    233:        u_int                            idx;
                    234:
                    235:        window_pane_index(wp, &idx);
                    236:
                    237:        item = window_tree_add_item(data);
                    238:        item->type = WINDOW_TREE_PANE;
                    239:        item->session = s->id;
                    240:        item->winlink = wl->idx;
                    241:        item->pane = wp->id;
                    242:
1.15      nicm      243:        text = format_single(NULL, data->format, NULL, s, wl, wp);
1.1       nicm      244:        xasprintf(&name, "%u", idx);
                    245:
                    246:        mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1);
                    247:        free(text);
                    248:        free(name);
                    249: }
                    250:
                    251: static int
1.16      nicm      252: window_tree_filter_pane(struct session *s, struct winlink *wl,
                    253:     struct window_pane *wp, const char *filter)
                    254: {
                    255:        char    *cp;
                    256:        int      result;
                    257:
                    258:        if (filter == NULL)
                    259:                return (1);
                    260:
                    261:        cp = format_single(NULL, filter, NULL, s, wl, wp);
                    262:        result = format_true(cp);
                    263:        free(cp);
                    264:
                    265:        return (result);
                    266: }
                    267:
                    268: static int
1.1       nicm      269: window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
1.5       nicm      270:     u_int sort_type, struct mode_tree_item *parent, const char *filter)
1.1       nicm      271: {
                    272:        struct window_tree_modedata     *data = modedata;
                    273:        struct window_tree_itemdata     *item;
                    274:        struct mode_tree_item           *mti;
1.16      nicm      275:        char                            *name, *text;
1.1       nicm      276:        struct window_pane              *wp, **l;
                    277:        u_int                            n, i;
                    278:        int                              expanded;
                    279:
                    280:        item = window_tree_add_item(data);
                    281:        item->type = WINDOW_TREE_WINDOW;
                    282:        item->session = s->id;
                    283:        item->winlink = wl->idx;
                    284:        item->pane = -1;
                    285:
1.15      nicm      286:        text = format_single(NULL, data->format, NULL, s, wl, NULL);
1.1       nicm      287:        xasprintf(&name, "%u", wl->idx);
                    288:
                    289:        if (data->type == WINDOW_TREE_SESSION ||
                    290:            data->type == WINDOW_TREE_WINDOW)
                    291:                expanded = 0;
                    292:        else
                    293:                expanded = 1;
                    294:        mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text,
                    295:            expanded);
                    296:        free(text);
                    297:        free(name);
                    298:
1.16      nicm      299:        wp = TAILQ_FIRST(&wl->window->panes);
                    300:        if (TAILQ_NEXT(wp, entry) == NULL) {
                    301:                if (!window_tree_filter_pane(s, wl, wp, filter))
                    302:                        goto empty;
1.13      nicm      303:                return (1);
1.16      nicm      304:        }
1.13      nicm      305:
1.1       nicm      306:        l = NULL;
                    307:        n = 0;
1.13      nicm      308:
1.1       nicm      309:        TAILQ_FOREACH(wp, &wl->window->panes, entry) {
1.16      nicm      310:                if (!window_tree_filter_pane(s, wl, wp, filter))
                    311:                        continue;
1.1       nicm      312:                l = xreallocarray(l, n + 1, sizeof *l);
                    313:                l[n++] = wp;
                    314:        }
1.16      nicm      315:        if (n == 0)
                    316:                goto empty;
1.1       nicm      317:
                    318:        switch (sort_type) {
                    319:        case WINDOW_TREE_BY_INDEX:
                    320:                break;
                    321:        case WINDOW_TREE_BY_NAME:
                    322:                /* Panes don't have names, so leave in number order. */
                    323:                break;
                    324:        case WINDOW_TREE_BY_TIME:
                    325:                qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
                    326:                break;
                    327:        }
                    328:
                    329:        for (i = 0; i < n; i++)
                    330:                window_tree_build_pane(s, wl, l[i], modedata, mti);
                    331:        free(l);
                    332:        return (1);
1.16      nicm      333:
                    334: empty:
                    335:        window_tree_free_item(item);
                    336:        data->item_size--;
                    337:        mode_tree_remove(data->data, mti);
                    338:        return (0);
1.1       nicm      339: }
                    340:
                    341: static void
                    342: window_tree_build_session(struct session *s, void* modedata,
1.5       nicm      343:     u_int sort_type, const char *filter)
1.1       nicm      344: {
                    345:        struct window_tree_modedata     *data = modedata;
                    346:        struct window_tree_itemdata     *item;
                    347:        struct mode_tree_item           *mti;
                    348:        char                            *text;
                    349:        struct winlink                  *wl, **l;
                    350:        u_int                            n, i, empty;
                    351:        int                              expanded;
                    352:
                    353:        item = window_tree_add_item(data);
                    354:        item->type = WINDOW_TREE_SESSION;
                    355:        item->session = s->id;
                    356:        item->winlink = -1;
                    357:        item->pane = -1;
                    358:
1.15      nicm      359:        text = format_single(NULL, data->format, NULL, s, NULL, NULL);
1.1       nicm      360:
                    361:        if (data->type == WINDOW_TREE_SESSION)
                    362:                expanded = 0;
                    363:        else
                    364:                expanded = 1;
                    365:        mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text,
                    366:            expanded);
                    367:        free(text);
                    368:
                    369:        l = NULL;
                    370:        n = 0;
                    371:        RB_FOREACH(wl, winlinks, &s->windows) {
                    372:                l = xreallocarray(l, n + 1, sizeof *l);
                    373:                l[n++] = wl;
                    374:        }
                    375:        switch (sort_type) {
                    376:        case WINDOW_TREE_BY_INDEX:
                    377:                break;
                    378:        case WINDOW_TREE_BY_NAME:
                    379:                qsort(l, n, sizeof *l, window_tree_cmp_window_name);
                    380:                break;
                    381:        case WINDOW_TREE_BY_TIME:
                    382:                qsort(l, n, sizeof *l, window_tree_cmp_window_time);
                    383:                break;
                    384:        }
                    385:
                    386:        empty = 0;
                    387:        for (i = 0; i < n; i++) {
                    388:                if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
1.5       nicm      389:                    filter))
1.1       nicm      390:                        empty++;
                    391:        }
                    392:        if (empty == n) {
                    393:                window_tree_free_item(item);
                    394:                data->item_size--;
                    395:                mode_tree_remove(data->data, mti);
                    396:        }
                    397:        free(l);
                    398: }
                    399:
                    400: static void
1.5       nicm      401: window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
                    402:     const char *filter)
1.1       nicm      403: {
                    404:        struct window_tree_modedata     *data = modedata;
                    405:        struct session                  *s, **l;
1.24      nicm      406:        struct session_group            *sg, *current;
1.1       nicm      407:        u_int                            n, i;
                    408:
1.24      nicm      409:        current = session_group_contains(data->fs.s);
                    410:
1.1       nicm      411:        for (i = 0; i < data->item_size; i++)
                    412:                window_tree_free_item(data->item_list[i]);
                    413:        free(data->item_list);
                    414:        data->item_list = NULL;
                    415:        data->item_size = 0;
                    416:
                    417:        l = NULL;
                    418:        n = 0;
                    419:        RB_FOREACH(s, sessions, &sessions) {
1.23      nicm      420:                if (data->squash_groups &&
1.24      nicm      421:                    (sg = session_group_contains(s)) != NULL) {
                    422:                        if ((sg == current && s != data->fs.s) ||
                    423:                            (sg != current && s != TAILQ_FIRST(&sg->sessions)))
                    424:                                continue;
                    425:                }
1.1       nicm      426:                l = xreallocarray(l, n + 1, sizeof *l);
                    427:                l[n++] = s;
                    428:        }
                    429:        switch (sort_type) {
                    430:        case WINDOW_TREE_BY_INDEX:
                    431:                break;
                    432:        case WINDOW_TREE_BY_NAME:
                    433:                qsort(l, n, sizeof *l, window_tree_cmp_session_name);
                    434:                break;
                    435:        case WINDOW_TREE_BY_TIME:
                    436:                qsort(l, n, sizeof *l, window_tree_cmp_session_time);
                    437:                break;
                    438:        }
                    439:
                    440:        for (i = 0; i < n; i++)
1.5       nicm      441:                window_tree_build_session(l[i], modedata, sort_type, filter);
1.1       nicm      442:        free(l);
                    443:
                    444:        switch (data->type) {
                    445:        case WINDOW_TREE_NONE:
                    446:                break;
                    447:        case WINDOW_TREE_SESSION:
                    448:                *tag = (uint64_t)data->fs.s;
                    449:                break;
                    450:        case WINDOW_TREE_WINDOW:
                    451:                *tag = (uint64_t)data->fs.wl;
                    452:                break;
                    453:        case WINDOW_TREE_PANE:
1.18      nicm      454:                if (window_count_panes(data->fs.wl->window) == 1)
                    455:                        *tag = (uint64_t)data->fs.wl;
                    456:                else
                    457:                        *tag = (uint64_t)data->fs.wp;
1.1       nicm      458:                break;
                    459:        }
                    460: }
                    461:
1.19      nicm      462:
                    463: static void
                    464: window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
                    465:     u_int sx, u_int sy, const struct grid_cell *gc, const char *label)
                    466: {
                    467:        size_t   len;
                    468:        u_int    ox, oy;
                    469:
                    470:        len = strlen(label);
                    471:        if (sx == 0 || sy == 1 || len > sx)
                    472:                return;
                    473:        ox = (sx - len + 1) / 2;
                    474:        oy = (sy + 1) / 2;
                    475:
                    476:        if (ox > 1 && ox + len < sx - 1 && sy >= 3) {
                    477:                screen_write_cursormove(ctx, px + ox - 1, py + oy - 1);
                    478:                screen_write_box(ctx, len + 2, 3);
                    479:        }
                    480:        screen_write_cursormove(ctx, px + ox, py + oy);
                    481:        screen_write_puts(ctx, gc, "%s", label);
                    482: }
                    483:
1.6       nicm      484: static void
1.11      nicm      485: window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
                    486:     struct screen_write_ctx *ctx, u_int sx, u_int sy)
1.6       nicm      487: {
                    488:        struct options          *oo = s->options;
                    489:        struct winlink          *wl;
                    490:        struct window           *w;
1.26      nicm      491:        u_int                    cx = ctx->s->cx, cy = ctx->s->cy;
1.9       nicm      492:        u_int                    loop, total, visible, each, width, offset;
                    493:        u_int                    current, start, end, remaining, i;
1.6       nicm      494:        struct grid_cell         gc;
1.9       nicm      495:        int                      colour, active_colour, left, right;
1.6       nicm      496:        char                    *label;
                    497:
1.9       nicm      498:        total = winlink_count(&s->windows);
1.6       nicm      499:
                    500:        memcpy(&gc, &grid_default_cell, sizeof gc);
                    501:        colour = options_get_number(oo, "display-panes-colour");
                    502:        active_colour = options_get_number(oo, "display-panes-active-colour");
                    503:
1.9       nicm      504:        if (sx / total < 24) {
                    505:                visible = sx / 24;
                    506:                if (visible == 0)
                    507:                        visible = 1;
                    508:        } else
                    509:                visible = total;
                    510:
                    511:        current = 0;
                    512:        RB_FOREACH(wl, winlinks, &s->windows) {
                    513:                if (wl == s->curw)
                    514:                        break;
                    515:                current++;
                    516:        }
1.6       nicm      517:
1.9       nicm      518:        if (current < visible) {
                    519:                start = 0;
                    520:                end = visible;
                    521:        } else if (current >= total - visible) {
                    522:                start = total - visible;
                    523:                end = total;
                    524:        } else {
                    525:                start = current - (visible / 2);
                    526:                end = start + visible;
                    527:        }
                    528:
1.11      nicm      529:        if (data->offset < -(int)start)
                    530:                data->offset = -(int)start;
                    531:        if (data->offset > (int)(total - end))
                    532:                data->offset = (int)(total - end);
                    533:        start += data->offset;
                    534:        end += data->offset;
                    535:
1.9       nicm      536:        left = (start != 0);
                    537:        right = (end != total);
                    538:        if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
                    539:                left = right = 0;
                    540:        if (left && right) {
                    541:                each = (sx - 6) / visible;
                    542:                remaining = (sx - 6) - (visible * each);
                    543:        } else if (left || right) {
                    544:                each = (sx - 3) / visible;
                    545:                remaining = (sx - 3) - (visible * each);
                    546:        } else {
                    547:                each = sx / visible;
                    548:                remaining = sx - (visible * each);
                    549:        }
                    550:        if (each == 0)
                    551:                return;
                    552:
                    553:        if (left) {
1.27    ! nicm      554:                data->left = cx + 2;
1.26      nicm      555:                screen_write_cursormove(ctx, cx + 2, cy);
1.9       nicm      556:                screen_write_vline(ctx, sy, 0, 0);
1.26      nicm      557:                screen_write_cursormove(ctx, cx, cy + sy / 2);
1.9       nicm      558:                screen_write_puts(ctx, &grid_default_cell, "<");
1.27    ! nicm      559:        } else
        !           560:                data->left = -1;
1.9       nicm      561:        if (right) {
1.27    ! nicm      562:                data->right = cx + sx - 3;
1.26      nicm      563:                screen_write_cursormove(ctx, cx + sx - 3, cy);
1.6       nicm      564:                screen_write_vline(ctx, sy, 0, 0);
1.26      nicm      565:                screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2);
1.9       nicm      566:                screen_write_puts(ctx, &grid_default_cell, ">");
1.27    ! nicm      567:        } else
        !           568:                data->right = -1;
        !           569:
        !           570:        data->start = start;
        !           571:        data->end = end;
        !           572:        data->each = each;
1.6       nicm      573:
1.9       nicm      574:        i = loop = 0;
                    575:        RB_FOREACH(wl, winlinks, &s->windows) {
                    576:                if (loop == end)
                    577:                        break;
                    578:                if (loop < start) {
                    579:                        loop++;
                    580:                        continue;
                    581:                }
                    582:                w = wl->window;
1.6       nicm      583:
                    584:                if (wl == s->curw)
                    585:                        gc.fg = active_colour;
                    586:                else
                    587:                        gc.fg = colour;
1.9       nicm      588:
                    589:                if (left)
                    590:                        offset = 3 + (i * each);
                    591:                else
                    592:                        offset = (i * each);
                    593:                if (loop == end - 1)
1.12      nicm      594:                        width = each + remaining;
1.6       nicm      595:                else
                    596:                        width = each - 1;
                    597:
1.26      nicm      598:                screen_write_cursormove(ctx, cx + offset, cy);
1.6       nicm      599:                screen_write_preview(ctx, &w->active->base, width, sy);
                    600:
                    601:                xasprintf(&label, " %u:%s ", wl->idx, w->name);
                    602:                if (strlen(label) > width)
                    603:                        xasprintf(&label, " %u ", wl->idx);
1.26      nicm      604:                window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc,
                    605:                    label);
1.6       nicm      606:                free(label);
                    607:
1.9       nicm      608:                if (loop != end - 1) {
1.26      nicm      609:                        screen_write_cursormove(ctx, cx + offset + width, cy);
1.6       nicm      610:                        screen_write_vline(ctx, sy, 0, 0);
                    611:                }
1.9       nicm      612:                loop++;
                    613:
                    614:                i++;
1.6       nicm      615:        }
                    616: }
                    617:
                    618: static void
1.11      nicm      619: window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
                    620:     struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy)
1.6       nicm      621: {
                    622:        struct options          *oo = s->options;
                    623:        struct window_pane      *wp;
1.26      nicm      624:        u_int                    cx = ctx->s->cx, cy = ctx->s->cy;
1.9       nicm      625:        u_int                    loop, total, visible, each, width, offset;
                    626:        u_int                    current, start, end, remaining, i;
1.6       nicm      627:        struct grid_cell         gc;
1.20      nicm      628:        int                      colour, active_colour, left, right, pane_idx;
1.6       nicm      629:        char                    *label;
                    630:
1.9       nicm      631:        total = window_count_panes(w);
1.6       nicm      632:
                    633:        memcpy(&gc, &grid_default_cell, sizeof gc);
                    634:        colour = options_get_number(oo, "display-panes-colour");
                    635:        active_colour = options_get_number(oo, "display-panes-active-colour");
                    636:
1.9       nicm      637:        if (sx / total < 24) {
                    638:                visible = sx / 24;
                    639:                if (visible == 0)
                    640:                        visible = 1;
                    641:        } else
                    642:                visible = total;
                    643:
                    644:        current = 0;
                    645:        TAILQ_FOREACH(wp, &w->panes, entry) {
                    646:                if (wp == w->active)
                    647:                        break;
                    648:                current++;
                    649:        }
                    650:
                    651:        if (current < visible) {
                    652:                start = 0;
                    653:                end = visible;
                    654:        } else if (current >= total - visible) {
                    655:                start = total - visible;
                    656:                end = total;
                    657:        } else {
                    658:                start = current - (visible / 2);
                    659:                end = start + visible;
                    660:        }
                    661:
1.11      nicm      662:        if (data->offset < -(int)start)
                    663:                data->offset = -(int)start;
                    664:        if (data->offset > (int)(total - end))
                    665:                data->offset = (int)(total - end);
                    666:        start += data->offset;
                    667:        end += data->offset;
                    668:
1.9       nicm      669:        left = (start != 0);
                    670:        right = (end != total);
                    671:        if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
                    672:                left = right = 0;
                    673:        if (left && right) {
                    674:                each = (sx - 6) / visible;
                    675:                remaining = (sx - 6) - (visible * each);
                    676:        } else if (left || right) {
                    677:                each = (sx - 3) / visible;
                    678:                remaining = (sx - 3) - (visible * each);
                    679:        } else {
                    680:                each = sx / visible;
                    681:                remaining = sx - (visible * each);
                    682:        }
                    683:        if (each == 0)
                    684:                return;
1.6       nicm      685:
1.9       nicm      686:        if (left) {
1.27    ! nicm      687:                data->left = cx + 2;
1.26      nicm      688:                screen_write_cursormove(ctx, cx + 2, cy);
1.6       nicm      689:                screen_write_vline(ctx, sy, 0, 0);
1.26      nicm      690:                screen_write_cursormove(ctx, cx, cy + sy / 2);
1.9       nicm      691:                screen_write_puts(ctx, &grid_default_cell, "<");
1.27    ! nicm      692:        } else
        !           693:                data->left = -1;
1.9       nicm      694:        if (right) {
1.27    ! nicm      695:                data->right = cx + sx - 3;
1.26      nicm      696:                screen_write_cursormove(ctx, cx + sx - 3, cy);
1.9       nicm      697:                screen_write_vline(ctx, sy, 0, 0);
1.26      nicm      698:                screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2);
1.9       nicm      699:                screen_write_puts(ctx, &grid_default_cell, ">");
1.27    ! nicm      700:        } else
        !           701:                data->right = -1;
        !           702:
        !           703:        data->start = start;
        !           704:        data->end = end;
        !           705:        data->each = each;
1.6       nicm      706:
1.9       nicm      707:        i = loop = 0;
                    708:        TAILQ_FOREACH(wp, &w->panes, entry) {
                    709:                if (loop == end)
                    710:                        break;
                    711:                if (loop < start) {
                    712:                        loop++;
                    713:                        continue;
                    714:                }
1.6       nicm      715:
                    716:                if (wp == w->active)
                    717:                        gc.fg = active_colour;
                    718:                else
                    719:                        gc.fg = colour;
1.9       nicm      720:
                    721:                if (left)
                    722:                        offset = 3 + (i * each);
                    723:                else
                    724:                        offset = (i * each);
                    725:                if (loop == end - 1)
1.12      nicm      726:                        width = each + remaining;
1.6       nicm      727:                else
                    728:                        width = each - 1;
                    729:
1.26      nicm      730:                screen_write_cursormove(ctx, cx + offset, cy);
1.6       nicm      731:                screen_write_preview(ctx, &wp->base, width, sy);
                    732:
1.20      nicm      733:                if (window_pane_index(wp, &pane_idx) != 0)
                    734:                        pane_idx = loop;
                    735:                xasprintf(&label, " %u ", pane_idx);
1.26      nicm      736:                window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc,
                    737:                    label);
1.6       nicm      738:                free(label);
                    739:
1.9       nicm      740:                if (loop != end - 1) {
1.26      nicm      741:                        screen_write_cursormove(ctx, cx + offset + width, cy);
1.6       nicm      742:                        screen_write_vline(ctx, sy, 0, 0);
                    743:                }
1.9       nicm      744:                loop++;
                    745:
                    746:                i++;
1.6       nicm      747:        }
                    748: }
                    749:
1.26      nicm      750: static void
                    751: window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx,
                    752:     u_int sx, u_int sy)
1.1       nicm      753: {
                    754:        struct window_tree_itemdata     *item = itemdata;
                    755:        struct session                  *sp;
                    756:        struct winlink                  *wlp;
                    757:        struct window_pane              *wp;
                    758:
                    759:        window_tree_pull_item(item, &sp, &wlp, &wp);
                    760:        if (wp == NULL)
1.26      nicm      761:                return;
1.1       nicm      762:
1.6       nicm      763:        switch (item->type) {
                    764:        case WINDOW_TREE_NONE:
1.26      nicm      765:                break;
1.6       nicm      766:        case WINDOW_TREE_SESSION:
1.26      nicm      767:                window_tree_draw_session(modedata, sp, ctx, sx, sy);
1.6       nicm      768:                break;
                    769:        case WINDOW_TREE_WINDOW:
1.26      nicm      770:                window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy);
1.6       nicm      771:                break;
                    772:        case WINDOW_TREE_PANE:
1.26      nicm      773:                screen_write_preview(ctx, &wp->base, sx, sy);
1.6       nicm      774:                break;
                    775:        }
1.1       nicm      776: }
                    777:
1.3       nicm      778: static int
                    779: window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
                    780: {
                    781:        struct window_tree_itemdata     *item = itemdata;
                    782:        struct session                  *s;
                    783:        struct winlink                  *wl;
                    784:        struct window_pane              *wp;
                    785:        const char                      *cmd;
                    786:
                    787:        window_tree_pull_item(item, &s, &wl, &wp);
                    788:
                    789:        switch (item->type) {
                    790:        case WINDOW_TREE_NONE:
                    791:                return (0);
                    792:        case WINDOW_TREE_SESSION:
                    793:                if (s == NULL)
                    794:                        return (0);
                    795:                return (strstr(s->name, ss) != NULL);
                    796:        case WINDOW_TREE_WINDOW:
                    797:                if (s == NULL || wl == NULL)
                    798:                        return (0);
                    799:                return (strstr(wl->window->name, ss) != NULL);
                    800:        case WINDOW_TREE_PANE:
                    801:                if (s == NULL || wl == NULL || wp == NULL)
                    802:                        break;
                    803:                cmd = get_proc_name(wp->fd, wp->tty);
                    804:                if (cmd == NULL || *cmd == '\0')
                    805:                        return (0);
                    806:                return (strstr(cmd, ss) != NULL);
                    807:        }
                    808:        return (0);
                    809: }
                    810:
1.1       nicm      811: static struct screen *
                    812: window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
                    813:     struct args *args)
                    814: {
                    815:        struct window_tree_modedata     *data;
                    816:        struct screen                   *s;
                    817:
                    818:        wp->modedata = data = xcalloc(1, sizeof *data);
                    819:
                    820:        if (args_has(args, 's'))
                    821:                data->type = WINDOW_TREE_SESSION;
                    822:        else if (args_has(args, 'w'))
                    823:                data->type = WINDOW_TREE_WINDOW;
                    824:        else
                    825:                data->type = WINDOW_TREE_PANE;
                    826:        memcpy(&data->fs, fs, sizeof data->fs);
                    827:
                    828:        data->wp = wp;
                    829:        data->references = 1;
                    830:
1.15      nicm      831:        if (args == NULL || !args_has(args, 'F'))
                    832:                data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
                    833:        else
                    834:                data->format = xstrdup(args_get(args, 'F'));
1.1       nicm      835:        if (args == NULL || args->argc == 0)
                    836:                data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
                    837:        else
                    838:                data->command = xstrdup(args->argv[0]);
1.23      nicm      839:        data->squash_groups = !args_has(args, 'G');
1.1       nicm      840:
1.4       nicm      841:        data->data = mode_tree_start(wp, args, window_tree_build,
                    842:            window_tree_draw, window_tree_search, data, window_tree_sort_list,
1.1       nicm      843:            nitems(window_tree_sort_list), &s);
                    844:
                    845:        mode_tree_build(data->data);
                    846:        mode_tree_draw(data->data);
                    847:
                    848:        data->type = WINDOW_TREE_NONE;
                    849:
                    850:        return (s);
                    851: }
                    852:
                    853: static void
                    854: window_tree_destroy(struct window_tree_modedata *data)
                    855: {
                    856:        u_int   i;
                    857:
                    858:        if (--data->references != 0)
                    859:                return;
                    860:
                    861:        mode_tree_free(data->data);
                    862:
                    863:        for (i = 0; i < data->item_size; i++)
                    864:                window_tree_free_item(data->item_list[i]);
                    865:        free(data->item_list);
                    866:
1.15      nicm      867:        free(data->format);
1.1       nicm      868:        free(data->command);
1.15      nicm      869:
1.1       nicm      870:        free(data);
                    871: }
                    872:
                    873: static void
                    874: window_tree_free(struct window_pane *wp)
                    875: {
                    876:        struct window_tree_modedata *data = wp->modedata;
                    877:
                    878:        if (data == NULL)
                    879:                return;
                    880:
                    881:        data->dead = 1;
                    882:        window_tree_destroy(data);
                    883: }
                    884:
                    885: static void
                    886: window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
                    887: {
                    888:        struct window_tree_modedata     *data = wp->modedata;
                    889:
                    890:        mode_tree_resize(data->data, sx, sy);
                    891: }
                    892:
                    893: static char *
                    894: window_tree_get_target(struct window_tree_itemdata *item,
                    895:     struct cmd_find_state *fs)
                    896: {
                    897:        struct session          *s;
                    898:        struct winlink          *wl;
                    899:        struct window_pane      *wp;
                    900:        char                    *target;
                    901:
                    902:        window_tree_pull_item(item, &s, &wl, &wp);
                    903:
                    904:        target = NULL;
                    905:        switch (item->type) {
                    906:        case WINDOW_TREE_NONE:
                    907:                break;
                    908:        case WINDOW_TREE_SESSION:
                    909:                if (s == NULL)
                    910:                        break;
                    911:                xasprintf(&target, "=%s:", s->name);
                    912:                break;
                    913:        case WINDOW_TREE_WINDOW:
                    914:                if (s == NULL || wl == NULL)
                    915:                        break;
                    916:                xasprintf(&target, "=%s:%u.", s->name, wl->idx);
                    917:                break;
                    918:        case WINDOW_TREE_PANE:
                    919:                if (s == NULL || wl == NULL || wp == NULL)
                    920:                        break;
                    921:                xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
                    922:                break;
                    923:        }
                    924:        if (target == NULL)
                    925:                cmd_find_clear_state(fs, 0);
                    926:        else
1.17      nicm      927:                cmd_find_from_winlink_pane(fs, wl, wp, 0);
1.1       nicm      928:        return (target);
                    929: }
                    930:
                    931: static void
1.21      nicm      932: window_tree_command_each(void* modedata, void* itemdata, struct client *c,
                    933:     __unused key_code key)
1.1       nicm      934: {
                    935:        struct window_tree_modedata     *data = modedata;
                    936:        struct window_tree_itemdata     *item = itemdata;
                    937:        char                            *name;
                    938:        struct cmd_find_state            fs;
                    939:
                    940:        name = window_tree_get_target(item, &fs);
                    941:        if (name != NULL)
1.21      nicm      942:                mode_tree_run_command(c, &fs, data->entered, name);
1.1       nicm      943:        free(name);
                    944: }
                    945:
                    946: static enum cmd_retval
                    947: window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
                    948: {
                    949:        struct window_tree_modedata     *data = modedata;
                    950:
                    951:        if (!data->dead) {
                    952:                mode_tree_build(data->data);
                    953:                mode_tree_draw(data->data);
                    954:                data->wp->flags |= PANE_REDRAW;
                    955:        }
                    956:        window_tree_destroy(data);
                    957:        return (CMD_RETURN_NORMAL);
                    958: }
                    959:
                    960: static int
                    961: window_tree_command_callback(struct client *c, void *modedata, const char *s,
                    962:     __unused int done)
                    963: {
                    964:        struct window_tree_modedata     *data = modedata;
                    965:
1.22      nicm      966:        if (s == NULL || data->dead)
1.1       nicm      967:                return (0);
                    968:
                    969:        data->entered = s;
1.21      nicm      970:        mode_tree_each_tagged(data->data, window_tree_command_each, c,
                    971:            KEYC_NONE, 1);
1.1       nicm      972:        data->entered = NULL;
                    973:
                    974:        data->references++;
                    975:        cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
                    976:
                    977:        return (0);
                    978: }
                    979:
                    980: static void
                    981: window_tree_command_free(void *modedata)
                    982: {
                    983:        struct window_tree_modedata     *data = modedata;
                    984:
                    985:        window_tree_destroy(data);
                    986: }
                    987:
1.27    ! nicm      988: static key_code
        !           989: window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x,
        !           990:     struct window_tree_itemdata *item)
        !           991: {
        !           992:        struct session          *s;
        !           993:        struct winlink          *wl;
        !           994:        struct window_pane      *wp;
        !           995:        u_int                    loop;
        !           996:
        !           997:        if (key != KEYC_MOUSEDOWN1_PANE)
        !           998:                return (KEYC_NONE);
        !           999:
        !          1000:        if (data->left != -1 && x <= (u_int)data->left)
        !          1001:                return ('<');
        !          1002:        if (data->right != -1 && x >= (u_int)data->right)
        !          1003:                return ('>');
        !          1004:
        !          1005:        if (data->left != -1)
        !          1006:                x -= data->left;
        !          1007:        else if (x != 0)
        !          1008:                x--;
        !          1009:        if (x == 0 || data->end == 0)
        !          1010:                x = 0;
        !          1011:        else {
        !          1012:                x = x / data->each;
        !          1013:                if (data->start + x >= data->end)
        !          1014:                        x = data->end - 1;
        !          1015:        }
        !          1016:
        !          1017:        window_tree_pull_item(item, &s, &wl, &wp);
        !          1018:        if (item->type == WINDOW_TREE_SESSION) {
        !          1019:                if (s == NULL)
        !          1020:                        return (KEYC_NONE);
        !          1021:                mode_tree_expand_current(data->data);
        !          1022:                loop = 0;
        !          1023:                RB_FOREACH(wl, winlinks, &s->windows) {
        !          1024:                        if (loop == data->start + x)
        !          1025:                                break;
        !          1026:                        loop++;
        !          1027:                }
        !          1028:                if (wl != NULL)
        !          1029:                        mode_tree_set_current(data->data, (uint64_t)wl);
        !          1030:                return ('\r');
        !          1031:        }
        !          1032:        if (item->type == WINDOW_TREE_WINDOW) {
        !          1033:                if (wl == NULL)
        !          1034:                        return (KEYC_NONE);
        !          1035:                mode_tree_expand_current(data->data);
        !          1036:                loop = 0;
        !          1037:                TAILQ_FOREACH(wp, &wl->window->panes, entry) {
        !          1038:                        if (loop == data->start + x)
        !          1039:                                break;
        !          1040:                        loop++;
        !          1041:                }
        !          1042:                if (wp != NULL)
        !          1043:                        mode_tree_set_current(data->data, (uint64_t)wp);
        !          1044:                return ('\r');
        !          1045:        }
        !          1046:        return (KEYC_NONE);
        !          1047: }
        !          1048:
1.1       nicm     1049: static void
                   1050: window_tree_key(struct window_pane *wp, struct client *c,
                   1051:     __unused struct session *s, key_code key, struct mouse_event *m)
                   1052: {
                   1053:        struct window_tree_modedata     *data = wp->modedata;
1.27    ! nicm     1054:        struct window_tree_itemdata     *item, *new_item;
1.21      nicm     1055:        char                            *name, *prompt;
1.1       nicm     1056:        struct cmd_find_state            fs;
                   1057:        int                              finished;
1.27    ! nicm     1058:        u_int                            tagged, x, y;
1.1       nicm     1059:
1.11      nicm     1060:        item = mode_tree_get_current(data->data);
1.27    ! nicm     1061:        finished = mode_tree_key(data->data, c, &key, m, &x, &y);
        !          1062:        if (item != (new_item = mode_tree_get_current(data->data))) {
        !          1063:                item = new_item;
1.11      nicm     1064:                data->offset = 0;
1.27    ! nicm     1065:        }
        !          1066:        if (KEYC_IS_MOUSE(key))
        !          1067:                key = window_tree_mouse(data, key, x, item);
1.1       nicm     1068:        switch (key) {
1.11      nicm     1069:        case '<':
                   1070:                data->offset--;
                   1071:                break;
                   1072:        case '>':
                   1073:                data->offset++;
                   1074:                break;
1.1       nicm     1075:        case ':':
                   1076:                tagged = mode_tree_count_tagged(data->data);
                   1077:                if (tagged != 0)
                   1078:                        xasprintf(&prompt, "(%u tagged) ", tagged);
                   1079:                else
                   1080:                        xasprintf(&prompt, "(current) ");
                   1081:                data->references++;
                   1082:                status_prompt_set(c, prompt, "", window_tree_command_callback,
                   1083:                    window_tree_command_free, data, PROMPT_NOFORMAT);
                   1084:                free(prompt);
                   1085:                break;
                   1086:        case '\r':
                   1087:                item = mode_tree_get_current(data->data);
                   1088:                name = window_tree_get_target(item, &fs);
                   1089:                if (name != NULL)
1.21      nicm     1090:                        mode_tree_run_command(c, NULL, data->command, name);
                   1091:                finished = 1;
1.1       nicm     1092:                free(name);
1.21      nicm     1093:                break;
1.1       nicm     1094:        }
                   1095:        if (finished)
                   1096:                window_pane_reset_mode(wp);
                   1097:        else {
                   1098:                mode_tree_draw(data->data);
                   1099:                wp->flags |= PANE_REDRAW;
                   1100:        }
                   1101: }