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

Annotation of src/usr.bin/tmux/menu.c, Revision 1.1

1.1     ! nicm        1: /* $OpenBSD$ */
        !             2:
        !             3: /*
        !             4:  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
        !             5:  *
        !             6:  * Permission to use, copy, modify, and distribute this software for any
        !             7:  * purpose with or without fee is hereby granted, provided that the above
        !             8:  * copyright notice and this permission notice appear in all copies.
        !             9:  *
        !            10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            14:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
        !            15:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
        !            16:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            17:  */
        !            18:
        !            19: #include <sys/types.h>
        !            20:
        !            21: #include <stdlib.h>
        !            22: #include <string.h>
        !            23:
        !            24: #include "tmux.h"
        !            25:
        !            26: struct menu_data {
        !            27:        struct cmdq_item        *item;
        !            28:        int                      flags;
        !            29:
        !            30:        struct cmd_find_state    fs;
        !            31:        struct screen            s;
        !            32:
        !            33:        u_int                    px;
        !            34:        u_int                    py;
        !            35:
        !            36:        struct menu             *menu;
        !            37:        int                      choice;
        !            38:
        !            39:        menu_choice_cb           cb;
        !            40:        void                    *data;
        !            41: };
        !            42:
        !            43: static void
        !            44: menu_add_item(struct menu *menu, struct menu_item *item, struct client *c,
        !            45:     struct cmd_find_state *fs)
        !            46: {
        !            47:        struct menu_item        *new_item;
        !            48:        const char              *key;
        !            49:        char                    *name;
        !            50:        u_int                    width;
        !            51:
        !            52:        menu->items = xreallocarray(menu->items, menu->count + 1,
        !            53:            sizeof *menu->items);
        !            54:        new_item = &menu->items[menu->count++];
        !            55:        memset(new_item, 0, sizeof *new_item);
        !            56:
        !            57:        if (item == NULL || *item->name == '\0') /* horizontal line */
        !            58:                return;
        !            59:        name = format_single(NULL, item->name, c, fs->s, fs->wl, fs->wp);
        !            60:        if (*name == '\0') { /* no item if empty after format expanded */
        !            61:                menu->count--;
        !            62:                return;
        !            63:        }
        !            64:        if (item->key != KEYC_UNKNOWN) {
        !            65:                key = key_string_lookup_key(item->key);
        !            66:                xasprintf(&new_item->name, "%s #[align=right](%s)", name, key);
        !            67:        } else
        !            68:                xasprintf(&new_item->name, "%s", name);
        !            69:        free(name);
        !            70:
        !            71:        if (item->command != NULL)
        !            72:                new_item->command = xstrdup(item->command);
        !            73:        else
        !            74:                new_item->command = NULL;
        !            75:        new_item->key = item->key;
        !            76:
        !            77:        width = format_width(new_item->name);
        !            78:        if (width > menu->width)
        !            79:                menu->width = width;
        !            80: }
        !            81:
        !            82: static void
        !            83: menu_parse_item(struct menu *menu, const char *s, struct client *c,
        !            84:     struct cmd_find_state *fs)
        !            85: {
        !            86:        char                    *copy, *first;
        !            87:        const char              *second, *third;
        !            88:        struct menu_item         item;
        !            89:
        !            90:        first = copy = xstrdup(s);
        !            91:        if ((second = format_skip(first, ",")) != NULL) {
        !            92:                *(char *)second++ = '\0';
        !            93:                if ((third = format_skip(second, ",")) != NULL) {
        !            94:                        *(char *)third++ = '\0';
        !            95:
        !            96:                        item.name = first;
        !            97:                        item.command = (char *)third;
        !            98:                        item.key = key_string_lookup_string(second);
        !            99:                        menu_add_item(menu, &item, c, fs);
        !           100:                }
        !           101:        }
        !           102:        free(copy);
        !           103: }
        !           104:
        !           105: struct menu *
        !           106: menu_create_from_items(struct menu_item *items, u_int count, struct client *c,
        !           107:     struct cmd_find_state *fs, const char *title)
        !           108: {
        !           109:        struct menu     *menu;
        !           110:        u_int            i;
        !           111:
        !           112:        menu = xcalloc(1, sizeof *menu);
        !           113:        menu->title = xstrdup(title);
        !           114:
        !           115:        for (i = 0; i < count; i++)
        !           116:                menu_add_item(menu, &items[i], c, fs);
        !           117:
        !           118:        return (menu);
        !           119: }
        !           120:
        !           121: struct menu *
        !           122: menu_create_from_string(const char *s, struct client *c,
        !           123:     struct cmd_find_state *fs, const char *title)
        !           124: {
        !           125:        struct menu     *menu;
        !           126:        char            *copy, *string, *next;
        !           127:
        !           128:        if (*s == '\0')
        !           129:                return (NULL);
        !           130:
        !           131:        menu = xcalloc(1, sizeof *menu);
        !           132:        menu->title = xstrdup(title);
        !           133:
        !           134:        copy = string = xstrdup(s);
        !           135:        do {
        !           136:                next = (char *)format_skip(string, "|");
        !           137:                log_debug("XXX %s -- %s", next, string);
        !           138:                if (next != NULL)
        !           139:                        *next++ = '\0';
        !           140:                if (*string == '\0')
        !           141:                        menu_add_item(menu, NULL, c, fs);
        !           142:                else
        !           143:                        menu_parse_item(menu, string, c, fs);
        !           144:                string = next;
        !           145:        } while (next != NULL);
        !           146:        free(copy);
        !           147:
        !           148:        return (menu);
        !           149: }
        !           150:
        !           151: void
        !           152: menu_free(struct menu *menu)
        !           153: {
        !           154:        u_int   i;
        !           155:
        !           156:        for (i = 0; i < menu->count; i++) {
        !           157:                free(menu->items[i].name);
        !           158:                free(menu->items[i].command);
        !           159:        }
        !           160:        free(menu->items);
        !           161:
        !           162:        free(menu->title);
        !           163:        free(menu);
        !           164: }
        !           165:
        !           166: static void
        !           167: menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0)
        !           168: {
        !           169:        struct menu_data        *md = c->overlay_data;
        !           170:        struct tty              *tty = &c->tty;
        !           171:        struct screen           *s = &md->s;
        !           172:        struct menu             *menu = md->menu;
        !           173:        struct screen_write_ctx  ctx;
        !           174:        u_int                    i, px, py;
        !           175:
        !           176:        screen_write_start(&ctx, NULL, s);
        !           177:        screen_write_clearscreen(&ctx, 8);
        !           178:        screen_write_menu(&ctx, menu, md->choice);
        !           179:        screen_write_stop(&ctx);
        !           180:
        !           181:        px = md->px;
        !           182:        py = md->py;
        !           183:
        !           184:        for (i = 0; i < screen_size_y(&md->s); i++)
        !           185:                tty_draw_line(tty, NULL, s, 0, i, menu->width + 4, px, py + i);
        !           186:
        !           187:        if (~md->flags & MENU_NOMOUSE)
        !           188:                tty_update_mode(tty, MODE_MOUSE_ALL, NULL);
        !           189: }
        !           190:
        !           191: static void
        !           192: menu_free_cb(struct client *c)
        !           193: {
        !           194:        struct menu_data        *md = c->overlay_data;
        !           195:
        !           196:        if (md->item != NULL)
        !           197:                md->item->flags &= ~CMDQ_WAITING;
        !           198:
        !           199:        screen_free(&md->s);
        !           200:        menu_free(md->menu);
        !           201:        free(md);
        !           202: }
        !           203:
        !           204: static enum cmd_retval
        !           205: menu_error_cb(struct cmdq_item *item, void *data)
        !           206: {
        !           207:        char    *error = data;
        !           208:
        !           209:        cmdq_error(item, "%s", error);
        !           210:        free(error);
        !           211:
        !           212:        return (CMD_RETURN_NORMAL);
        !           213: }
        !           214:
        !           215: static int
        !           216: menu_key_cb(struct client *c, struct key_event *event)
        !           217: {
        !           218:        struct menu_data                *md = c->overlay_data;
        !           219:        struct menu                     *menu = md->menu;
        !           220:        struct mouse_event              *m = &event->m;
        !           221:        u_int                            i;
        !           222:        int                              count = menu->count, old = md->choice;
        !           223:        const struct menu_item          *item;
        !           224:        struct cmd_list                 *cmdlist;
        !           225:        struct cmdq_item                *new_item;
        !           226:        char                            *cause;
        !           227:
        !           228:        if (KEYC_IS_MOUSE(event->key)) {
        !           229:                if (md->flags & MENU_NOMOUSE)
        !           230:                        return (0);
        !           231:                if (m->x < md->px ||
        !           232:                    m->x > md->px + 4 + menu->width ||
        !           233:                    m->y < md->py + 1 ||
        !           234:                    m->y > md->py + 1 + count - 1) {
        !           235:                        if (MOUSE_RELEASE(m->b))
        !           236:                                return (1);
        !           237:                        if (md->choice != -1) {
        !           238:                                md->choice = -1;
        !           239:                                c->flags |= CLIENT_REDRAWOVERLAY;
        !           240:                        }
        !           241:                        return (0);
        !           242:                }
        !           243:                md->choice = m->y - (md->py + 1);
        !           244:                if (MOUSE_RELEASE(m->b))
        !           245:                        goto chosen;
        !           246:                if (md->choice != old)
        !           247:                        c->flags |= CLIENT_REDRAWOVERLAY;
        !           248:                return (0);
        !           249:        }
        !           250:        switch (event->key) {
        !           251:        case KEYC_UP:
        !           252:                do {
        !           253:                        if (md->choice == -1 || md->choice == 0)
        !           254:                                md->choice = count - 1;
        !           255:                        else
        !           256:                                md->choice--;
        !           257:                } while (menu->items[md->choice].name == NULL);
        !           258:                c->flags |= CLIENT_REDRAWOVERLAY;
        !           259:                return (0);
        !           260:        case KEYC_DOWN:
        !           261:                do {
        !           262:                        if (md->choice == -1 || md->choice == count - 1)
        !           263:                                md->choice = 0;
        !           264:                else
        !           265:                        md->choice++;
        !           266:                } while (menu->items[md->choice].name == NULL);
        !           267:                c->flags |= CLIENT_REDRAWOVERLAY;
        !           268:                return (0);
        !           269:        case '\r':
        !           270:                goto chosen;
        !           271:        case '\033': /* Escape */
        !           272:        case '\003': /* C-c */
        !           273:        case '\007': /* C-g */
        !           274:        case 'q':
        !           275:                return (1);
        !           276:        }
        !           277:        for (i = 0; i < (u_int)count; i++) {
        !           278:                if (event->key == menu->items[i].key) {
        !           279:                        md->choice = i;
        !           280:                        goto chosen;
        !           281:                }
        !           282:        }
        !           283:        return (0);
        !           284:
        !           285: chosen:
        !           286:        if (md->choice == -1)
        !           287:                return (1);
        !           288:        item = &menu->items[md->choice];
        !           289:        if (item->name == NULL)
        !           290:                return (1);
        !           291:        if (md->cb != NULL) {
        !           292:            md->cb(md->menu, md->choice, item->key, md->data);
        !           293:            return (1);
        !           294:        }
        !           295:        cmdlist = cmd_string_parse(item->command, NULL, 0, &cause);
        !           296:        if (cmdlist == NULL && cause != NULL)
        !           297:                new_item = cmdq_get_callback(menu_error_cb, cause);
        !           298:        else if (cmdlist == NULL)
        !           299:                new_item = NULL;
        !           300:        else {
        !           301:                new_item = cmdq_get_command(cmdlist, &md->fs, NULL, 0);
        !           302:                cmd_list_free(cmdlist);
        !           303:        }
        !           304:        if (new_item != NULL) {
        !           305:                if (md->item != NULL)
        !           306:                        cmdq_insert_after(md->item, new_item);
        !           307:                else
        !           308:                        cmdq_append(c, new_item);
        !           309:        }
        !           310:        return (1);
        !           311: }
        !           312:
        !           313: int
        !           314: menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
        !           315:     u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb,
        !           316:     void *data)
        !           317: {
        !           318:        struct menu_data        *md;
        !           319:
        !           320:        if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2)
        !           321:                return (-1);
        !           322:
        !           323:        md = xcalloc(1, sizeof *md);
        !           324:        md->item = item;
        !           325:        md->flags = flags;
        !           326:
        !           327:        cmd_find_copy_state(&md->fs, fs);
        !           328:        screen_init(&md->s, menu->width + 4, menu->count + 2, 0);
        !           329:
        !           330:        md->px = px;
        !           331:        md->py = py;
        !           332:
        !           333:        md->menu = menu;
        !           334:        md->choice = -1;
        !           335:
        !           336:        md->cb = cb;
        !           337:        md->data = data;
        !           338:
        !           339:        server_client_set_overlay(c, 0, menu_draw_cb, menu_key_cb, menu_free_cb,
        !           340:            md);
        !           341:        return (0);
        !           342: }