Annotation of embedaddon/tmux/window-choose.c, revision 1.1

1.1     ! misho       1: /* $OpenBSD$ */
        !             2: 
        !             3: /*
        !             4:  * Copyright (c) 2009 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 <stdlib.h>
        !            23: #include <string.h>
        !            24: 
        !            25: #include "tmux.h"
        !            26: 
        !            27: static struct screen *window_choose_init(struct window_pane *);
        !            28: static void    window_choose_free(struct window_pane *);
        !            29: static void    window_choose_resize(struct window_pane *, u_int, u_int);
        !            30: static void    window_choose_key(struct window_pane *, struct client *,
        !            31:                    struct session *, key_code, struct mouse_event *);
        !            32: 
        !            33: static void    window_choose_default_callback(struct window_choose_data *);
        !            34: static struct window_choose_mode_item *window_choose_get_item(
        !            35:                    struct window_pane *, key_code, struct mouse_event *);
        !            36: 
        !            37: static void    window_choose_fire_callback(struct window_pane *,
        !            38:                    struct window_choose_data *);
        !            39: static void    window_choose_redraw_screen(struct window_pane *);
        !            40: static void    window_choose_write_line(struct window_pane *,
        !            41:                    struct screen_write_ctx *, u_int);
        !            42: 
        !            43: static void    window_choose_scroll_up(struct window_pane *);
        !            44: static void    window_choose_scroll_down(struct window_pane *);
        !            45: 
        !            46: static void    window_choose_collapse(struct window_pane *, struct session *,
        !            47:                    u_int);
        !            48: static void    window_choose_expand(struct window_pane *, struct session *,
        !            49:                    u_int);
        !            50: static void    window_choose_collapse_all(struct window_pane *);
        !            51: 
        !            52: static void    window_choose_data_free(struct window_choose_data *);
        !            53: 
        !            54: enum window_choose_input_type {
        !            55:        WINDOW_CHOOSE_NORMAL = -1,
        !            56:        WINDOW_CHOOSE_GOTO_ITEM,
        !            57: };
        !            58: 
        !            59: const struct window_mode window_choose_mode = {
        !            60:        .init = window_choose_init,
        !            61:        .free = window_choose_free,
        !            62:        .resize = window_choose_resize,
        !            63:        .key = window_choose_key,
        !            64: };
        !            65: 
        !            66: struct window_choose_mode_item {
        !            67:        struct window_choose_data       *wcd;
        !            68:        char                            *name;
        !            69:        int                              pos;
        !            70:        int                              state;
        !            71: #define TREE_EXPANDED 0x1
        !            72: };
        !            73: 
        !            74: struct window_choose_mode_data {
        !            75:        struct screen           screen;
        !            76: 
        !            77:        struct window_choose_mode_item *list;
        !            78:        u_int                   list_size;
        !            79:        struct window_choose_mode_item *old_list;
        !            80:        u_int                   old_list_size;
        !            81: 
        !            82:        int                     width;
        !            83:        u_int                   top;
        !            84:        u_int                   selected;
        !            85:        enum window_choose_input_type input_type;
        !            86:        const char              *input_prompt;
        !            87:        char                    *input_str;
        !            88: 
        !            89:        void                    (*callbackfn)(struct window_choose_data *);
        !            90: };
        !            91: 
        !            92: static const char window_choose_keys_emacs[] = "0123456789"
        !            93:                                               "abcdefghijklmnoprstuvwxyz"
        !            94:                                               "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        !            95: static const char window_choose_keys_vi[] = "0123456789"
        !            96:                                            "abcdefimnoprstuvwxyz"
        !            97:                                            "ABCDEFIJKMNOPQRSTUVWXYZ";
        !            98: 
        !            99: static void    window_choose_free1(struct window_choose_mode_data *);
        !           100: static int     window_choose_key_index(struct window_pane *, u_int);
        !           101: static int     window_choose_index_key(struct window_pane *, key_code);
        !           102: static void    window_choose_prompt_input(enum window_choose_input_type,
        !           103:                    const char *, struct window_pane *, key_code);
        !           104: static void    window_choose_reset_top(struct window_pane *, u_int);
        !           105: 
        !           106: void
        !           107: window_choose_add(struct window_pane *wp, struct window_choose_data *wcd)
        !           108: {
        !           109:        struct window_choose_mode_data  *data = wp->modedata;
        !           110:        struct window_choose_mode_item  *item;
        !           111:        char                             tmp[11];
        !           112: 
        !           113:        data->list = xreallocarray(data->list, data->list_size + 1,
        !           114:            sizeof *data->list);
        !           115:        item = &data->list[data->list_size++];
        !           116: 
        !           117:        item->name = format_expand(wcd->ft, wcd->ft_template);
        !           118:        item->wcd = wcd;
        !           119:        item->pos = data->list_size - 1;
        !           120:        item->state = 0;
        !           121: 
        !           122:        data->width = xsnprintf(tmp, sizeof tmp, "%d", item->pos);
        !           123: }
        !           124: 
        !           125: void
        !           126: window_choose_set_current(struct window_pane *wp, u_int cur)
        !           127: {
        !           128:        struct window_choose_mode_data  *data = wp->modedata;
        !           129:        struct screen                   *s = &data->screen;
        !           130: 
        !           131:        data->selected = cur;
        !           132:        window_choose_reset_top(wp, screen_size_y(s));
        !           133: }
        !           134: 
        !           135: static void
        !           136: window_choose_reset_top(struct window_pane *wp, u_int sy)
        !           137: {
        !           138:        struct window_choose_mode_data  *data = wp->modedata;
        !           139: 
        !           140:        data->top = 0;
        !           141:        if (data->selected > sy - 1)
        !           142:                data->top = data->selected - (sy - 1);
        !           143: 
        !           144:        window_choose_redraw_screen(wp);
        !           145: }
        !           146: 
        !           147: void
        !           148: window_choose_ready(struct window_pane *wp, u_int cur,
        !           149:     void (*callbackfn)(struct window_choose_data *))
        !           150: {
        !           151:        struct window_choose_mode_data  *data = wp->modedata;
        !           152:        u_int                            size;
        !           153: 
        !           154:        data->callbackfn = callbackfn;
        !           155:        if (data->callbackfn == NULL)
        !           156:                data->callbackfn = window_choose_default_callback;
        !           157: 
        !           158:        size = data->old_list_size;
        !           159:        data->old_list_size += data->list_size;
        !           160:        data->old_list = xreallocarray(data->old_list, data->old_list_size,
        !           161:            sizeof *data->old_list);
        !           162:        memcpy(data->old_list + size, data->list, data->list_size *
        !           163:            sizeof *data->list);
        !           164: 
        !           165:        window_choose_set_current(wp, cur);
        !           166:        window_choose_collapse_all(wp);
        !           167: }
        !           168: 
        !           169: static struct screen *
        !           170: window_choose_init(struct window_pane *wp)
        !           171: {
        !           172:        struct window_choose_mode_data  *data;
        !           173:        struct screen                   *s;
        !           174: 
        !           175:        wp->modedata = data = xcalloc(1, sizeof *data);
        !           176: 
        !           177:        data->callbackfn = NULL;
        !           178:        data->input_type = WINDOW_CHOOSE_NORMAL;
        !           179:        data->input_str = xstrdup("");
        !           180:        data->input_prompt = NULL;
        !           181: 
        !           182:        data->list = NULL;
        !           183:        data->list_size = 0;
        !           184: 
        !           185:        data->old_list = NULL;
        !           186:        data->old_list_size = 0;
        !           187: 
        !           188:        data->top = 0;
        !           189: 
        !           190:        s = &data->screen;
        !           191:        screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
        !           192:        s->mode &= ~MODE_CURSOR;
        !           193: 
        !           194:        return (s);
        !           195: }
        !           196: 
        !           197: struct window_choose_data *
        !           198: window_choose_data_create(int type, struct client *c, struct session *s)
        !           199: {
        !           200:        struct window_choose_data       *wcd;
        !           201: 
        !           202:        wcd = xmalloc(sizeof *wcd);
        !           203:        wcd->type = type;
        !           204: 
        !           205:        wcd->ft = format_create(NULL, FORMAT_NONE, 0);
        !           206:        wcd->ft_template = NULL;
        !           207: 
        !           208:        wcd->command = NULL;
        !           209: 
        !           210:        wcd->wl = NULL;
        !           211:        wcd->pane_id = -1;
        !           212:        wcd->idx = -1;
        !           213: 
        !           214:        wcd->tree_session = NULL;
        !           215: 
        !           216:        wcd->start_client = c;
        !           217:        wcd->start_client->references++;
        !           218:        wcd->start_session = s;
        !           219:        wcd->start_session->references++;
        !           220: 
        !           221:        return (wcd);
        !           222: }
        !           223: 
        !           224: static void
        !           225: window_choose_data_free(struct window_choose_data *wcd)
        !           226: {
        !           227:        server_client_unref(wcd->start_client);
        !           228:        session_unref(wcd->start_session);
        !           229: 
        !           230:        if (wcd->tree_session != NULL)
        !           231:                session_unref(wcd->tree_session);
        !           232: 
        !           233:        free(wcd->ft_template);
        !           234:        format_free(wcd->ft);
        !           235: 
        !           236:        free(wcd->command);
        !           237:        free(wcd);
        !           238: }
        !           239: 
        !           240: void
        !           241: window_choose_data_run(struct window_choose_data *cdata)
        !           242: {
        !           243:        struct cmd_list         *cmdlist;
        !           244:        char                    *cause;
        !           245:        struct cmdq_item        *item;
        !           246: 
        !           247:        /*
        !           248:         * The command template will have already been replaced. But if it's
        !           249:         * NULL, bail here.
        !           250:         */
        !           251:        if (cdata->command == NULL)
        !           252:                return;
        !           253: 
        !           254:        cmdlist = cmd_string_parse(cdata->command, NULL, 0, &cause);
        !           255:        if (cmdlist == NULL) {
        !           256:                if (cause != NULL) {
        !           257:                        *cause = toupper((u_char) *cause);
        !           258:                        status_message_set(cdata->start_client, "%s", cause);
        !           259:                        free(cause);
        !           260:                }
        !           261:                return;
        !           262:        }
        !           263: 
        !           264:        item = cmdq_get_command(cmdlist, NULL, NULL, 0);
        !           265:        cmdq_append(cdata->start_client, item);
        !           266:        cmd_list_free(cmdlist);
        !           267: }
        !           268: 
        !           269: static void
        !           270: window_choose_default_callback(struct window_choose_data *wcd)
        !           271: {
        !           272:        if (wcd == NULL)
        !           273:                return;
        !           274:        if (wcd->start_client->flags & CLIENT_DEAD)
        !           275:                return;
        !           276: 
        !           277:        window_choose_data_run(wcd);
        !           278: }
        !           279: 
        !           280: static void
        !           281: window_choose_free(struct window_pane *wp)
        !           282: {
        !           283:        if (wp->modedata != NULL)
        !           284:                window_choose_free1(wp->modedata);
        !           285: }
        !           286: 
        !           287: static void
        !           288: window_choose_free1(struct window_choose_mode_data *data)
        !           289: {
        !           290:        struct window_choose_mode_item  *item;
        !           291:        u_int                            i;
        !           292: 
        !           293:        if (data == NULL)
        !           294:                return;
        !           295: 
        !           296:        for (i = 0; i < data->old_list_size; i++) {
        !           297:                item = &data->old_list[i];
        !           298:                window_choose_data_free(item->wcd);
        !           299:                free(item->name);
        !           300:        }
        !           301:        free(data->list);
        !           302:        free(data->old_list);
        !           303: 
        !           304:        free(data->input_str);
        !           305: 
        !           306:        screen_free(&data->screen);
        !           307:        free(data);
        !           308: }
        !           309: 
        !           310: static void
        !           311: window_choose_resize(struct window_pane *wp, u_int sx, u_int sy)
        !           312: {
        !           313:        struct window_choose_mode_data  *data = wp->modedata;
        !           314:        struct screen                   *s = &data->screen;
        !           315: 
        !           316:        window_choose_reset_top(wp, sy);
        !           317:        screen_resize(s, sx, sy, 0);
        !           318:        window_choose_redraw_screen(wp);
        !           319: }
        !           320: 
        !           321: static void
        !           322: window_choose_fire_callback(struct window_pane *wp,
        !           323:     struct window_choose_data *wcd)
        !           324: {
        !           325:        struct window_choose_mode_data  *data = wp->modedata;
        !           326: 
        !           327:        wp->modedata = NULL;
        !           328:        window_pane_reset_mode(wp);
        !           329: 
        !           330:        data->callbackfn(wcd);
        !           331: 
        !           332:        window_choose_free1(data);
        !           333: }
        !           334: 
        !           335: static void
        !           336: window_choose_prompt_input(enum window_choose_input_type input_type,
        !           337:     const char *prompt, struct window_pane *wp, key_code key)
        !           338: {
        !           339:        struct window_choose_mode_data  *data = wp->modedata;
        !           340:        size_t                           input_len;
        !           341: 
        !           342:        data->input_type = input_type;
        !           343:        data->input_prompt = prompt;
        !           344:        input_len = strlen(data->input_str) + 2;
        !           345: 
        !           346:        data->input_str = xrealloc(data->input_str, input_len);
        !           347:        data->input_str[input_len - 2] = key;
        !           348:        data->input_str[input_len - 1] = '\0';
        !           349: 
        !           350:        window_choose_redraw_screen(wp);
        !           351: }
        !           352: 
        !           353: static void
        !           354: window_choose_collapse(struct window_pane *wp, struct session *s, u_int pos)
        !           355: {
        !           356:        struct window_choose_mode_data  *data = wp->modedata;
        !           357:        struct window_choose_mode_item  *item, *chosen, *copy = NULL;
        !           358:        struct window_choose_data       *wcd;
        !           359:        u_int                            i, copy_size = 0;
        !           360: 
        !           361:        chosen = &data->list[pos];
        !           362:        chosen->state &= ~TREE_EXPANDED;
        !           363: 
        !           364:        /*
        !           365:         * Trying to mangle the &data->list in-place has lots of problems, so
        !           366:         * assign the actual result we want to render and copy the new one over
        !           367:         * the top of it.
        !           368:         */
        !           369:        for (i = 0; i < data->list_size; i++) {
        !           370:                item = &data->list[i];
        !           371:                wcd = item->wcd;
        !           372: 
        !           373:                if (s == wcd->tree_session) {
        !           374:                        /* We only show the session when collapsed. */
        !           375:                        if (wcd->type & TREE_SESSION) {
        !           376:                                item->state &= ~TREE_EXPANDED;
        !           377: 
        !           378:                                copy = xreallocarray(copy, copy_size + 1,
        !           379:                                    sizeof *copy);
        !           380:                                memcpy(&copy[copy_size], item, sizeof *copy);
        !           381:                                copy_size++;
        !           382: 
        !           383:                                /*
        !           384:                                 * Update the selection to this session item so
        !           385:                                 * we don't end up highlighting a non-existent
        !           386:                                 * item.
        !           387:                                 */
        !           388:                                data->selected = i;
        !           389:                        }
        !           390:                } else {
        !           391:                        copy = xreallocarray(copy, copy_size + 1, sizeof *copy);
        !           392:                        memcpy(&copy[copy_size], item, sizeof *copy);
        !           393:                        copy_size++;
        !           394:                }
        !           395:        }
        !           396: 
        !           397:        if (copy_size != 0) {
        !           398:                free(data->list);
        !           399:                data->list = copy;
        !           400:                data->list_size = copy_size;
        !           401:        }
        !           402: }
        !           403: 
        !           404: static void
        !           405: window_choose_collapse_all(struct window_pane *wp)
        !           406: {
        !           407:        struct window_choose_mode_data  *data = wp->modedata;
        !           408:        struct window_choose_mode_item  *item;
        !           409:        struct screen                   *scr = &data->screen;
        !           410:        struct session                  *s, *chosen;
        !           411:        u_int                            i;
        !           412: 
        !           413:        chosen = data->list[data->selected].wcd->start_session;
        !           414: 
        !           415:        RB_FOREACH(s, sessions, &sessions)
        !           416:                window_choose_collapse(wp, s, data->selected);
        !           417: 
        !           418:        /* Reset the selection back to the starting session. */
        !           419:        for (i = 0; i < data->list_size; i++) {
        !           420:                item = &data->list[i];
        !           421: 
        !           422:                if (chosen != item->wcd->tree_session)
        !           423:                        continue;
        !           424: 
        !           425:                if (item->wcd->type & TREE_SESSION)
        !           426:                        data->selected = i;
        !           427:        }
        !           428:        window_choose_reset_top(wp, screen_size_y(scr));
        !           429: }
        !           430: 
        !           431: void
        !           432: window_choose_expand_all(struct window_pane *wp)
        !           433: {
        !           434:        struct window_choose_mode_data  *data = wp->modedata;
        !           435:        struct window_choose_mode_item  *item;
        !           436:        struct screen                   *scr = &data->screen;
        !           437:        struct session                  *s;
        !           438:        u_int                            i;
        !           439: 
        !           440:        RB_FOREACH(s, sessions, &sessions) {
        !           441:                for (i = 0; i < data->list_size; i++) {
        !           442:                        item = &data->list[i];
        !           443: 
        !           444:                        if (s != item->wcd->tree_session)
        !           445:                                continue;
        !           446: 
        !           447:                        if (item->wcd->type & TREE_SESSION)
        !           448:                                window_choose_expand(wp, s, i);
        !           449:                }
        !           450:        }
        !           451: 
        !           452:        window_choose_reset_top(wp, screen_size_y(scr));
        !           453: }
        !           454: 
        !           455: static void
        !           456: window_choose_expand(struct window_pane *wp, struct session *s, u_int pos)
        !           457: {
        !           458:        struct window_choose_mode_data  *data = wp->modedata;
        !           459:        struct window_choose_mode_item  *item, *chosen;
        !           460:        struct window_choose_data       *wcd;
        !           461:        u_int                            i, items;
        !           462: 
        !           463:        chosen = &data->list[pos];
        !           464:        items = data->old_list_size - 1;
        !           465: 
        !           466:        /* It's not possible to expand anything other than sessions. */
        !           467:        if (!(chosen->wcd->type & TREE_SESSION))
        !           468:                return;
        !           469: 
        !           470:        /* Don't re-expand a session which is already expanded. */
        !           471:        if (chosen->state & TREE_EXPANDED)
        !           472:                return;
        !           473: 
        !           474:        /* Mark the session entry as expanded. */
        !           475:        chosen->state |= TREE_EXPANDED;
        !           476: 
        !           477:        /*
        !           478:         * Go back through the original list of all sessions and windows, and
        !           479:         * pull out the windows where the session matches the selection chosen
        !           480:         * to expand.
        !           481:         */
        !           482:        for (i = items; i > 0; i--) {
        !           483:                item = &data->old_list[i];
        !           484:                item->state |= TREE_EXPANDED;
        !           485:                wcd = item->wcd;
        !           486: 
        !           487:                if (s == wcd->tree_session) {
        !           488:                        /*
        !           489:                         * Since the session is already displayed, we only care
        !           490:                         * to add back in window for it.
        !           491:                         */
        !           492:                        if (wcd->type & TREE_WINDOW) {
        !           493:                                /*
        !           494:                                 * If the insertion point for adding the
        !           495:                                 * windows to the session falls inside the
        !           496:                                 * range of the list, then we insert these
        !           497:                                 * entries in order *AFTER* the selected
        !           498:                                 * session.
        !           499:                                 */
        !           500:                                if (pos < i) {
        !           501:                                        data->list = xreallocarray(data->list,
        !           502:                                            data->list_size + 1,
        !           503:                                            sizeof *data->list);
        !           504:                                        memmove(&data->list[pos + 2],
        !           505:                                            &data->list[pos + 1],
        !           506:                                            (data->list_size - (pos + 1)) *
        !           507:                                            sizeof *data->list);
        !           508:                                        memcpy(&data->list[pos + 1],
        !           509:                                            &data->old_list[i],
        !           510:                                            sizeof *data->list);
        !           511:                                        data->list_size++;
        !           512:                                } else {
        !           513:                                        /* Ran out of room, add to the end. */
        !           514:                                        data->list = xreallocarray(data->list,
        !           515:                                            data->list_size + 1,
        !           516:                                            sizeof *data->list);
        !           517:                                        memcpy(&data->list[data->list_size],
        !           518:                                            &data->old_list[i],
        !           519:                                            sizeof *data->list);
        !           520:                                        data->list_size++;
        !           521:                                }
        !           522:                        }
        !           523:                }
        !           524:        }
        !           525: }
        !           526: 
        !           527: static struct window_choose_mode_item *
        !           528: window_choose_get_item(struct window_pane *wp, key_code key,
        !           529:     struct mouse_event *m)
        !           530: {
        !           531:        struct window_choose_mode_data  *data = wp->modedata;
        !           532:        u_int                            x, y, idx;
        !           533: 
        !           534:        if (!KEYC_IS_MOUSE(key))
        !           535:                return (&data->list[data->selected]);
        !           536: 
        !           537:        if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
        !           538:                return (NULL);
        !           539: 
        !           540:        idx = data->top + y;
        !           541:        if (idx >= data->list_size)
        !           542:                return (NULL);
        !           543:        return (&data->list[idx]);
        !           544: }
        !           545: 
        !           546: static key_code
        !           547: window_choose_translate_key(key_code key)
        !           548: {
        !           549:        switch (key) {
        !           550:        case '0'|KEYC_ESCAPE:
        !           551:        case '1'|KEYC_ESCAPE:
        !           552:        case '2'|KEYC_ESCAPE:
        !           553:        case '3'|KEYC_ESCAPE:
        !           554:        case '4'|KEYC_ESCAPE:
        !           555:        case '5'|KEYC_ESCAPE:
        !           556:        case '6'|KEYC_ESCAPE:
        !           557:        case '7'|KEYC_ESCAPE:
        !           558:        case '8'|KEYC_ESCAPE:
        !           559:        case '9'|KEYC_ESCAPE:
        !           560:        case '\003': /* C-c */
        !           561:        case 'q':
        !           562:        case '\n':
        !           563:        case '\r':
        !           564:        case KEYC_BSPACE:
        !           565:        case ' ':
        !           566:        case KEYC_LEFT|KEYC_CTRL:
        !           567:        case KEYC_RIGHT|KEYC_CTRL:
        !           568:        case KEYC_MOUSEDOWN1_PANE:
        !           569:        case KEYC_MOUSEDOWN3_PANE:
        !           570:        case KEYC_WHEELUP_PANE:
        !           571:        case KEYC_WHEELDOWN_PANE:
        !           572:                return (key);
        !           573:        case '\031': /* C-y */
        !           574:        case KEYC_UP|KEYC_CTRL:
        !           575:                return (KEYC_UP|KEYC_CTRL);
        !           576:        case '\002': /* C-b */
        !           577:        case KEYC_PPAGE:
        !           578:                return (KEYC_PPAGE);
        !           579:        case '\005': /* C-e */
        !           580:        case KEYC_DOWN|KEYC_CTRL:
        !           581:                return (KEYC_DOWN|KEYC_CTRL);
        !           582:        case '\006': /* C-f */
        !           583:        case KEYC_NPAGE:
        !           584:                return (KEYC_NPAGE);
        !           585:        case 'h':
        !           586:        case KEYC_LEFT:
        !           587:                return (KEYC_LEFT);
        !           588:        case 'j':
        !           589:        case KEYC_DOWN:
        !           590:                return (KEYC_DOWN);
        !           591:        case 'k':
        !           592:        case KEYC_UP:
        !           593:                return (KEYC_UP);
        !           594:        case 'l':
        !           595:        case KEYC_RIGHT:
        !           596:                return (KEYC_RIGHT);
        !           597:        case 'g':
        !           598:        case KEYC_HOME:
        !           599:                return (KEYC_HOME);
        !           600:        case 'G':
        !           601:        case KEYC_END:
        !           602:                return (KEYC_END);
        !           603:        case 'H':
        !           604:                return ('R'|KEYC_ESCAPE);
        !           605:        case 'L':
        !           606:                return ('r'|KEYC_ESCAPE);
        !           607:        }
        !           608:        if ((key >= '0' && key <= '9') ||
        !           609:            (key >= 'a' && key <= 'z') ||
        !           610:            (key >= 'A' && key <= 'Z'))
        !           611:                return (key);
        !           612:        return (KEYC_NONE);
        !           613: }
        !           614: 
        !           615: static void
        !           616: window_choose_key(struct window_pane *wp, __unused struct client *c,
        !           617:     __unused struct session *sp, key_code key, struct mouse_event *m)
        !           618: {
        !           619:        struct window_choose_mode_data  *data = wp->modedata;
        !           620:        struct screen                   *s = &data->screen;
        !           621:        struct screen_write_ctx          ctx;
        !           622:        struct window_choose_mode_item  *item;
        !           623:        size_t                           input_len;
        !           624:        u_int                            items, n;
        !           625:        int                              idx, keys;
        !           626: 
        !           627:        keys = options_get_number(wp->window->options, "mode-keys");
        !           628:        if (keys == MODEKEY_VI) {
        !           629:                key = window_choose_translate_key(key);
        !           630:                if (key == KEYC_NONE)
        !           631:                        return;
        !           632:        }
        !           633:        items = data->list_size;
        !           634: 
        !           635:        if (data->input_type == WINDOW_CHOOSE_GOTO_ITEM) {
        !           636:                switch (key) {
        !           637:                case '\003': /* C-c */
        !           638:                case '\033': /* Escape */
        !           639:                case 'q':
        !           640:                        data->input_type = WINDOW_CHOOSE_NORMAL;
        !           641:                        window_choose_redraw_screen(wp);
        !           642:                        break;
        !           643:                case '\n':
        !           644:                case '\r':
        !           645:                        n = strtonum(data->input_str, 0, INT_MAX, NULL);
        !           646:                        if (n > items - 1) {
        !           647:                                data->input_type = WINDOW_CHOOSE_NORMAL;
        !           648:                                window_choose_redraw_screen(wp);
        !           649:                                break;
        !           650:                        }
        !           651:                        window_choose_fire_callback(wp, data->list[n].wcd);
        !           652:                        break;
        !           653:                case KEYC_BSPACE:
        !           654:                        input_len = strlen(data->input_str);
        !           655:                        if (input_len > 0)
        !           656:                                data->input_str[input_len - 1] = '\0';
        !           657:                        window_choose_redraw_screen(wp);
        !           658:                        break;
        !           659:                default:
        !           660:                        if (key < '0' || key > '9')
        !           661:                                break;
        !           662:                        window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
        !           663:                            "Goto Item", wp, key);
        !           664:                        break;
        !           665:                }
        !           666:                return;
        !           667:        }
        !           668: 
        !           669:        switch (key) {
        !           670:        case '\003': /* C-c */
        !           671:        case '\033': /* Escape */
        !           672:        case 'q':
        !           673:                window_choose_fire_callback(wp, NULL);
        !           674:                break;
        !           675:        case '\n':
        !           676:        case '\r':
        !           677:        case KEYC_MOUSEDOWN1_PANE:
        !           678:                if ((item = window_choose_get_item(wp, key, m)) == NULL)
        !           679:                        break;
        !           680:                window_choose_fire_callback(wp, item->wcd);
        !           681:                break;
        !           682:        case ' ':
        !           683:        case KEYC_MOUSEDOWN3_PANE:
        !           684:                if ((item = window_choose_get_item(wp, key, m)) == NULL)
        !           685:                        break;
        !           686:                if (item->state & TREE_EXPANDED) {
        !           687:                        window_choose_collapse(wp, item->wcd->tree_session,
        !           688:                            data->selected);
        !           689:                } else {
        !           690:                        window_choose_expand(wp, item->wcd->tree_session,
        !           691:                            data->selected);
        !           692:                }
        !           693:                window_choose_redraw_screen(wp);
        !           694:                break;
        !           695:        case KEYC_LEFT:
        !           696:                if ((item = window_choose_get_item(wp, key, m)) == NULL)
        !           697:                        break;
        !           698:                if (item->state & TREE_EXPANDED) {
        !           699:                        window_choose_collapse(wp, item->wcd->tree_session,
        !           700:                            data->selected);
        !           701:                        window_choose_redraw_screen(wp);
        !           702:                }
        !           703:                break;
        !           704:        case KEYC_LEFT|KEYC_CTRL:
        !           705:                window_choose_collapse_all(wp);
        !           706:                break;
        !           707:        case KEYC_RIGHT:
        !           708:                if ((item = window_choose_get_item(wp, key, m)) == NULL)
        !           709:                        break;
        !           710:                if (!(item->state & TREE_EXPANDED)) {
        !           711:                        window_choose_expand(wp, item->wcd->tree_session,
        !           712:                            data->selected);
        !           713:                        window_choose_redraw_screen(wp);
        !           714:                }
        !           715:                break;
        !           716:        case KEYC_RIGHT|KEYC_CTRL:
        !           717:                window_choose_expand_all(wp);
        !           718:                break;
        !           719:        case '\020': /* C-p */
        !           720:        case KEYC_UP:
        !           721:        case KEYC_WHEELUP_PANE:
        !           722:                if (items == 0)
        !           723:                        break;
        !           724:                if (data->selected == 0) {
        !           725:                        data->selected = items - 1;
        !           726:                        if (data->selected > screen_size_y(s) - 1)
        !           727:                                data->top = items - screen_size_y(s);
        !           728:                        window_choose_redraw_screen(wp);
        !           729:                        break;
        !           730:                }
        !           731:                data->selected--;
        !           732:                if (data->selected < data->top)
        !           733:                        window_choose_scroll_up(wp);
        !           734:                else {
        !           735:                        screen_write_start(&ctx, wp, NULL);
        !           736:                        window_choose_write_line(wp, &ctx,
        !           737:                            data->selected - data->top);
        !           738:                        window_choose_write_line(wp, &ctx,
        !           739:                            data->selected + 1 - data->top);
        !           740:                        screen_write_stop(&ctx);
        !           741:                }
        !           742:                break;
        !           743:        case '\016': /* C-n */
        !           744:        case KEYC_DOWN:
        !           745:        case KEYC_WHEELDOWN_PANE:
        !           746:                if (items == 0)
        !           747:                        break;
        !           748:                if (data->selected == items - 1) {
        !           749:                        data->selected = 0;
        !           750:                        data->top = 0;
        !           751:                        window_choose_redraw_screen(wp);
        !           752:                        break;
        !           753:                }
        !           754:                data->selected++;
        !           755: 
        !           756:                if (data->selected < data->top + screen_size_y(s)) {
        !           757:                        screen_write_start(&ctx, wp, NULL);
        !           758:                        window_choose_write_line(wp, &ctx,
        !           759:                            data->selected - data->top);
        !           760:                        window_choose_write_line(wp, &ctx,
        !           761:                            data->selected - 1 - data->top);
        !           762:                        screen_write_stop(&ctx);
        !           763:                } else
        !           764:                        window_choose_scroll_down(wp);
        !           765:                break;
        !           766:        case KEYC_UP|KEYC_CTRL:
        !           767:                if (items == 0 || data->top == 0)
        !           768:                        break;
        !           769:                if (data->selected == data->top + screen_size_y(s) - 1) {
        !           770:                        data->selected--;
        !           771:                        window_choose_scroll_up(wp);
        !           772:                        screen_write_start(&ctx, wp, NULL);
        !           773:                        window_choose_write_line(wp, &ctx,
        !           774:                            screen_size_y(s) - 1);
        !           775:                        screen_write_stop(&ctx);
        !           776:                } else
        !           777:                        window_choose_scroll_up(wp);
        !           778:                break;
        !           779:        case KEYC_DOWN|KEYC_CTRL:
        !           780:                if (items == 0 ||
        !           781:                    data->top + screen_size_y(&data->screen) >= items)
        !           782:                        break;
        !           783:                if (data->selected == data->top) {
        !           784:                        data->selected++;
        !           785:                        window_choose_scroll_down(wp);
        !           786:                        screen_write_start(&ctx, wp, NULL);
        !           787:                        window_choose_write_line(wp, &ctx, 0);
        !           788:                        screen_write_stop(&ctx);
        !           789:                } else
        !           790:                        window_choose_scroll_down(wp);
        !           791:                break;
        !           792:        case KEYC_PPAGE:
        !           793:                if (data->selected < screen_size_y(s)) {
        !           794:                        data->selected = 0;
        !           795:                        data->top = 0;
        !           796:                } else {
        !           797:                        data->selected -= screen_size_y(s);
        !           798:                        if (data->top < screen_size_y(s))
        !           799:                                data->top = 0;
        !           800:                        else
        !           801:                                data->top -= screen_size_y(s);
        !           802:                }
        !           803:                window_choose_redraw_screen(wp);
        !           804:                break;
        !           805:        case KEYC_NPAGE:
        !           806:                data->selected += screen_size_y(s);
        !           807:                if (data->selected > items - 1)
        !           808:                        data->selected = items - 1;
        !           809:                data->top += screen_size_y(s);
        !           810:                if (screen_size_y(s) < items) {
        !           811:                        if (data->top + screen_size_y(s) > items)
        !           812:                                data->top = items - screen_size_y(s);
        !           813:                } else
        !           814:                        data->top = 0;
        !           815:                if (data->selected < data->top)
        !           816:                        data->top = data->selected;
        !           817:                window_choose_redraw_screen(wp);
        !           818:                break;
        !           819:        case KEYC_BSPACE:
        !           820:                input_len = strlen(data->input_str);
        !           821:                if (input_len > 0)
        !           822:                        data->input_str[input_len - 1] = '\0';
        !           823:                window_choose_redraw_screen(wp);
        !           824:                break;
        !           825:        case '0'|KEYC_ESCAPE:
        !           826:        case '1'|KEYC_ESCAPE:
        !           827:        case '2'|KEYC_ESCAPE:
        !           828:        case '3'|KEYC_ESCAPE:
        !           829:        case '4'|KEYC_ESCAPE:
        !           830:        case '5'|KEYC_ESCAPE:
        !           831:        case '6'|KEYC_ESCAPE:
        !           832:        case '7'|KEYC_ESCAPE:
        !           833:        case '8'|KEYC_ESCAPE:
        !           834:        case '9'|KEYC_ESCAPE:
        !           835:                key &= KEYC_MASK_KEY;
        !           836:                if (key < '0' || key > '9')
        !           837:                        break;
        !           838:                window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
        !           839:                    "Goto Item", wp, key);
        !           840:                break;
        !           841:        case KEYC_HOME:
        !           842:        case '<'|KEYC_ESCAPE:
        !           843:                data->selected = 0;
        !           844:                data->top = 0;
        !           845:                window_choose_redraw_screen(wp);
        !           846:                break;
        !           847:        case 'R'|KEYC_ESCAPE:
        !           848:                data->selected = data->top;
        !           849:                window_choose_redraw_screen(wp);
        !           850:                break;
        !           851:        case 'r'|KEYC_ESCAPE:
        !           852:                data->selected = data->top + screen_size_y(s) - 1;
        !           853:                if (data->selected > items - 1)
        !           854:                        data->selected = items - 1;
        !           855:                window_choose_redraw_screen(wp);
        !           856:                break;
        !           857:        case KEYC_END:
        !           858:        case '>'|KEYC_ESCAPE:
        !           859:                data->selected = items - 1;
        !           860:                if (screen_size_y(s) < items)
        !           861:                        data->top = items - screen_size_y(s);
        !           862:                else
        !           863:                        data->top = 0;
        !           864:                window_choose_redraw_screen(wp);
        !           865:                break;
        !           866:        default:
        !           867:                idx = window_choose_index_key(wp, key);
        !           868:                if (idx < 0 || (u_int) idx >= data->list_size)
        !           869:                        break;
        !           870:                data->selected = idx;
        !           871:                window_choose_fire_callback(wp, data->list[idx].wcd);
        !           872:                break;
        !           873:        }
        !           874: }
        !           875: 
        !           876: static void
        !           877: window_choose_write_line(struct window_pane *wp, struct screen_write_ctx *ctx,
        !           878:     u_int py)
        !           879: {
        !           880:        struct window_choose_mode_data  *data = wp->modedata;
        !           881:        struct window_choose_mode_item  *item;
        !           882:        struct options                  *oo = wp->window->options;
        !           883:        struct screen                   *s = &data->screen;
        !           884:        struct grid_cell                 gc;
        !           885:        size_t                           last, xoff = 0;
        !           886:        char                             hdr[32], label[32];
        !           887:        int                              key;
        !           888: 
        !           889:        if (data->callbackfn == NULL)
        !           890:                fatalx("called before callback assigned");
        !           891: 
        !           892:        last = screen_size_y(s) - 1;
        !           893:        memcpy(&gc, &grid_default_cell, sizeof gc);
        !           894:        gc.flags |= GRID_FLAG_NOPALETTE;
        !           895:        if (data->selected == data->top + py)
        !           896:                style_apply(&gc, oo, "mode-style");
        !           897: 
        !           898:        screen_write_cursormove(ctx, 0, py);
        !           899:        if (data->top + py  < data->list_size) {
        !           900:                item = &data->list[data->top + py];
        !           901:                if (item->wcd->wl != NULL &&
        !           902:                    item->wcd->wl->flags & WINLINK_ALERTFLAGS)
        !           903:                        gc.attr |= GRID_ATTR_BRIGHT;
        !           904: 
        !           905:                key = window_choose_key_index(wp, data->top + py);
        !           906:                if (key != -1)
        !           907:                        xsnprintf(label, sizeof label, "(%c)", key);
        !           908:                else
        !           909:                        xsnprintf(label, sizeof label, "(%d)", item->pos);
        !           910:                screen_write_nputs(ctx, screen_size_x(s) - 1, &gc,
        !           911:                    "%*s %s %s", data->width + 2, label,
        !           912:                    /*
        !           913:                     * Add indication to tree if necessary about whether it's
        !           914:                     * expanded or not.
        !           915:                     */
        !           916:                    (item->wcd->type & TREE_SESSION) ?
        !           917:                    ((item->state & TREE_EXPANDED) ? "-" : "+") : "", item->name);
        !           918:        }
        !           919:        while (s->cx < screen_size_x(s) - 1)
        !           920:                screen_write_putc(ctx, &gc, ' ');
        !           921: 
        !           922:        if (data->input_type != WINDOW_CHOOSE_NORMAL) {
        !           923:                style_apply(&gc, oo, "mode-style");
        !           924: 
        !           925:                xoff = xsnprintf(hdr, sizeof hdr,
        !           926:                        "%s: %s", data->input_prompt, data->input_str);
        !           927:                screen_write_cursormove(ctx, 0, last);
        !           928:                screen_write_puts(ctx, &gc, "%s", hdr);
        !           929:                screen_write_cursormove(ctx, xoff, py);
        !           930:                memcpy(&gc, &grid_default_cell, sizeof gc);
        !           931:        }
        !           932: 
        !           933: }
        !           934: 
        !           935: static int
        !           936: window_choose_key_index(struct window_pane *wp, u_int idx)
        !           937: {
        !           938:        const char      *ptr;
        !           939:        int              keys;
        !           940: 
        !           941:        keys = options_get_number(wp->window->options, "mode-keys");
        !           942:        if (keys == MODEKEY_VI)
        !           943:                ptr = window_choose_keys_vi;
        !           944:        else
        !           945:                ptr = window_choose_keys_emacs;
        !           946:        for (; *ptr != '\0'; ptr++) {
        !           947:                if (idx-- == 0)
        !           948:                        return (*ptr);
        !           949:        }
        !           950:        return (-1);
        !           951: }
        !           952: 
        !           953: static int
        !           954: window_choose_index_key(struct window_pane *wp, key_code key)
        !           955: {
        !           956:        const char      *ptr;
        !           957:        int              keys;
        !           958:        u_int            idx = 0;
        !           959: 
        !           960:        keys = options_get_number(wp->window->options, "mode-keys");
        !           961:        if (keys == MODEKEY_VI)
        !           962:                ptr = window_choose_keys_vi;
        !           963:        else
        !           964:                ptr = window_choose_keys_emacs;
        !           965:        for (; *ptr != '\0'; ptr++) {
        !           966:                if (key == (key_code)*ptr)
        !           967:                        return (idx);
        !           968:                idx++;
        !           969:        }
        !           970:        return (-1);
        !           971: }
        !           972: 
        !           973: static void
        !           974: window_choose_redraw_screen(struct window_pane *wp)
        !           975: {
        !           976:        struct window_choose_mode_data  *data = wp->modedata;
        !           977:        struct screen                   *s = &data->screen;
        !           978:        struct screen_write_ctx          ctx;
        !           979:        u_int                            i;
        !           980: 
        !           981:        screen_write_start(&ctx, wp, NULL);
        !           982:        for (i = 0; i < screen_size_y(s); i++)
        !           983:                window_choose_write_line(wp, &ctx, i);
        !           984:        screen_write_stop(&ctx);
        !           985: }
        !           986: 
        !           987: static void
        !           988: window_choose_scroll_up(struct window_pane *wp)
        !           989: {
        !           990:        struct window_choose_mode_data  *data = wp->modedata;
        !           991:        struct screen_write_ctx          ctx;
        !           992: 
        !           993:        if (data->top == 0)
        !           994:                return;
        !           995:        data->top--;
        !           996: 
        !           997:        screen_write_start(&ctx, wp, NULL);
        !           998:        screen_write_cursormove(&ctx, 0, 0);
        !           999:        screen_write_insertline(&ctx, 1, 8);
        !          1000:        window_choose_write_line(wp, &ctx, 0);
        !          1001:        if (screen_size_y(&data->screen) > 1)
        !          1002:                window_choose_write_line(wp, &ctx, 1);
        !          1003:        screen_write_stop(&ctx);
        !          1004: }
        !          1005: 
        !          1006: static void
        !          1007: window_choose_scroll_down(struct window_pane *wp)
        !          1008: {
        !          1009:        struct window_choose_mode_data  *data = wp->modedata;
        !          1010:        struct screen                   *s = &data->screen;
        !          1011:        struct screen_write_ctx          ctx;
        !          1012: 
        !          1013:        if (data->top >= data->list_size)
        !          1014:                return;
        !          1015:        data->top++;
        !          1016: 
        !          1017:        screen_write_start(&ctx, wp, NULL);
        !          1018:        screen_write_cursormove(&ctx, 0, 0);
        !          1019:        screen_write_deleteline(&ctx, 1, 8);
        !          1020:        window_choose_write_line(wp, &ctx, screen_size_y(s) - 1);
        !          1021:        if (screen_size_y(&data->screen) > 1)
        !          1022:                window_choose_write_line(wp, &ctx, screen_size_y(s) - 2);
        !          1023:        screen_write_stop(&ctx);
        !          1024: }
        !          1025: 
        !          1026: struct window_choose_data *
        !          1027: window_choose_add_session(struct window_pane *wp, struct client *c,
        !          1028:     struct session *s, const char *template, const char *action, u_int idx)
        !          1029: {
        !          1030:        struct window_choose_data       *wcd;
        !          1031: 
        !          1032:        wcd = window_choose_data_create(TREE_SESSION, c, c->session);
        !          1033:        wcd->idx = s->id;
        !          1034: 
        !          1035:        wcd->tree_session = s;
        !          1036:        wcd->tree_session->references++;
        !          1037: 
        !          1038:        wcd->ft_template = xstrdup(template);
        !          1039:        format_add(wcd->ft, "line", "%u", idx);
        !          1040:        format_defaults(wcd->ft, NULL, s, NULL, NULL);
        !          1041: 
        !          1042:        wcd->command = cmd_template_replace(action, s->name, 1);
        !          1043: 
        !          1044:        window_choose_add(wp, wcd);
        !          1045: 
        !          1046:        return (wcd);
        !          1047: }
        !          1048: 
        !          1049: struct window_choose_data *
        !          1050: window_choose_add_window(struct window_pane *wp, struct client *c,
        !          1051:     struct session *s, struct winlink *wl, const char *template,
        !          1052:     const char *action, u_int idx)
        !          1053: {
        !          1054:        struct window_choose_data       *wcd;
        !          1055:        char                            *expanded;
        !          1056: 
        !          1057:        wcd = window_choose_data_create(TREE_WINDOW, c, c->session);
        !          1058:        wcd->idx = wl->idx;
        !          1059: 
        !          1060:        wcd->wl = wl;
        !          1061: 
        !          1062:        wcd->tree_session = s;
        !          1063:        wcd->tree_session->references++;
        !          1064: 
        !          1065:        wcd->ft_template = xstrdup(template);
        !          1066:        format_add(wcd->ft, "line", "%u", idx);
        !          1067:        format_defaults(wcd->ft, NULL, s, wl, NULL);
        !          1068: 
        !          1069:        xasprintf(&expanded, "%s:%d", s->name, wl->idx);
        !          1070:        wcd->command = cmd_template_replace(action, expanded, 1);
        !          1071:        free(expanded);
        !          1072: 
        !          1073:        window_choose_add(wp, wcd);
        !          1074: 
        !          1075:        return (wcd);
        !          1076: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>