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

Annotation of src/usr.bin/tmux/window-choose.c, Revision 1.77

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