1*f3288aa0Snicm /* $OpenBSD: menu.c,v 1.54 2024/10/17 17:10:41 nicm Exp $ */ 23546f4c9Snicm 33546f4c9Snicm /* 43546f4c9Snicm * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> 53546f4c9Snicm * 63546f4c9Snicm * Permission to use, copy, modify, and distribute this software for any 73546f4c9Snicm * purpose with or without fee is hereby granted, provided that the above 83546f4c9Snicm * copyright notice and this permission notice appear in all copies. 93546f4c9Snicm * 103546f4c9Snicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 113546f4c9Snicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 123546f4c9Snicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 133546f4c9Snicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 143546f4c9Snicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 153546f4c9Snicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 163546f4c9Snicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 173546f4c9Snicm */ 183546f4c9Snicm 193546f4c9Snicm #include <sys/types.h> 203546f4c9Snicm 213546f4c9Snicm #include <stdlib.h> 223546f4c9Snicm #include <string.h> 233546f4c9Snicm 243546f4c9Snicm #include "tmux.h" 253546f4c9Snicm 263546f4c9Snicm struct menu_data { 273546f4c9Snicm struct cmdq_item *item; 283546f4c9Snicm int flags; 293546f4c9Snicm 3017d7ce67Snicm struct grid_cell style; 3117d7ce67Snicm struct grid_cell border_style; 3219c94b00Snicm struct grid_cell selected_style; 3317d7ce67Snicm enum box_lines border_lines; 3417d7ce67Snicm 353546f4c9Snicm struct cmd_find_state fs; 363546f4c9Snicm struct screen s; 373546f4c9Snicm 383546f4c9Snicm u_int px; 393546f4c9Snicm u_int py; 403546f4c9Snicm 413546f4c9Snicm struct menu *menu; 423546f4c9Snicm int choice; 433546f4c9Snicm 443546f4c9Snicm menu_choice_cb cb; 453546f4c9Snicm void *data; 463546f4c9Snicm }; 473546f4c9Snicm 481335341aSnicm void 491335341aSnicm menu_add_items(struct menu *menu, const struct menu_item *items, 501335341aSnicm struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) 511335341aSnicm { 521335341aSnicm const struct menu_item *loop; 531335341aSnicm 541335341aSnicm for (loop = items; loop->name != NULL; loop++) 551335341aSnicm menu_add_item(menu, loop, qitem, c, fs); 561335341aSnicm } 571335341aSnicm 581335341aSnicm void 591335341aSnicm menu_add_item(struct menu *menu, const struct menu_item *item, 607db4c597Snicm struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) 613546f4c9Snicm { 623546f4c9Snicm struct menu_item *new_item; 63c6c9946fSnicm const char *key = NULL, *cmd, *suffix = ""; 64310774ddSnicm char *s, *trimmed, *name; 65c6c9946fSnicm u_int width, max_width; 661335341aSnicm int line; 67c6c9946fSnicm size_t keylen, slen; 681335341aSnicm 691335341aSnicm line = (item == NULL || item->name == NULL || *item->name == '\0'); 701335341aSnicm if (line && menu->count == 0) 711335341aSnicm return; 72422252e0Snicm if (line && menu->items[menu->count - 1].name == NULL) 73422252e0Snicm return; 743546f4c9Snicm 753546f4c9Snicm menu->items = xreallocarray(menu->items, menu->count + 1, 763546f4c9Snicm sizeof *menu->items); 773546f4c9Snicm new_item = &menu->items[menu->count++]; 783546f4c9Snicm memset(new_item, 0, sizeof *new_item); 793546f4c9Snicm 801335341aSnicm if (line) 813546f4c9Snicm return; 821335341aSnicm 831335341aSnicm if (fs != NULL) 84bf46d0ceSnicm s = format_single_from_state(qitem, item->name, c, fs); 851335341aSnicm else 861335341aSnicm s = format_single(qitem, item->name, c, NULL, NULL, NULL); 871335341aSnicm if (*s == '\0') { /* no item if empty after format expanded */ 883546f4c9Snicm menu->count--; 893546f4c9Snicm return; 903546f4c9Snicm } 91c6c9946fSnicm max_width = c->tty.sx - 4; 92c6c9946fSnicm 93c6c9946fSnicm slen = strlen(s); 94f05a5af3Snicm if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) { 955416581eSnicm key = key_string_lookup_key(item->key, 0); 96c6c9946fSnicm keylen = strlen(key) + 3; /* 3 = space and two brackets */ 97c6c9946fSnicm 98c6c9946fSnicm /* 99d4e11490Snicm * Add the key if it is shorter than a quarter of the available 100d4e11490Snicm * space or there is space for the entire item text and the 101d4e11490Snicm * key. 102c6c9946fSnicm */ 103d4e11490Snicm if (keylen <= max_width / 4) 104d4e11490Snicm max_width -= keylen; 105d4e11490Snicm else if (keylen >= max_width || slen >= max_width - keylen) 106c6c9946fSnicm key = NULL; 107c6c9946fSnicm } 108c6c9946fSnicm 109c6c9946fSnicm if (slen > max_width) { 110c6c9946fSnicm max_width--; 111c6c9946fSnicm suffix = ">"; 112c6c9946fSnicm } 113310774ddSnicm trimmed = format_trim_right(s, max_width); 114310774ddSnicm if (key != NULL) { 115310774ddSnicm xasprintf(&name, "%s%s#[default] #[align=right](%s)", 116310774ddSnicm trimmed, suffix, key); 117310774ddSnicm } else 118310774ddSnicm xasprintf(&name, "%s%s", trimmed, suffix); 119310774ddSnicm free(trimmed); 120d4e11490Snicm 1211335341aSnicm new_item->name = name; 1221335341aSnicm free(s); 1233546f4c9Snicm 1241335341aSnicm cmd = item->command; 1251335341aSnicm if (cmd != NULL) { 1261335341aSnicm if (fs != NULL) 127bf46d0ceSnicm s = format_single_from_state(qitem, cmd, c, fs); 1281335341aSnicm else 1291335341aSnicm s = format_single(qitem, cmd, c, NULL, NULL, NULL); 1307db4c597Snicm } else 1311335341aSnicm s = NULL; 1321335341aSnicm new_item->command = s; 1333546f4c9Snicm new_item->key = item->key; 1343546f4c9Snicm 1353546f4c9Snicm width = format_width(new_item->name); 13631f1d149Snicm if (*new_item->name == '-') 13731f1d149Snicm width--; 1383546f4c9Snicm if (width > menu->width) 1393546f4c9Snicm menu->width = width; 1403546f4c9Snicm } 1413546f4c9Snicm 1423546f4c9Snicm struct menu * 1431335341aSnicm menu_create(const char *title) 1443546f4c9Snicm { 1453546f4c9Snicm struct menu *menu; 1463546f4c9Snicm 1473546f4c9Snicm menu = xcalloc(1, sizeof *menu); 1483546f4c9Snicm menu->title = xstrdup(title); 1492746cc45Snicm menu->width = format_width(title); 1503546f4c9Snicm 1513546f4c9Snicm return (menu); 1523546f4c9Snicm } 1533546f4c9Snicm 1543546f4c9Snicm void 1553546f4c9Snicm menu_free(struct menu *menu) 1563546f4c9Snicm { 1573546f4c9Snicm u_int i; 1583546f4c9Snicm 1593546f4c9Snicm for (i = 0; i < menu->count; i++) { 1601335341aSnicm free((void *)menu->items[i].name); 1611335341aSnicm free((void *)menu->items[i].command); 1623546f4c9Snicm } 1633546f4c9Snicm free(menu->items); 1643546f4c9Snicm 1651335341aSnicm free((void *)menu->title); 1663546f4c9Snicm free(menu); 1673546f4c9Snicm } 1683546f4c9Snicm 169f38784aeSnicm struct screen * 17076013b85Snicm menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy) 1710cac3d2dSnicm { 172f38784aeSnicm struct menu_data *md = data; 1730cac3d2dSnicm 17476013b85Snicm *cx = md->px + 2; 17576013b85Snicm if (md->choice == -1) 17676013b85Snicm *cy = md->py; 17776013b85Snicm else 17876013b85Snicm *cy = md->py + 1 + md->choice; 17976013b85Snicm 18083e83a91Snicm return (&md->s); 1810cac3d2dSnicm } 1820cac3d2dSnicm 1837f16de32Snicm /* Return parts of the input range which are not obstructed by the menu. */ 1847f16de32Snicm void 1857f16de32Snicm menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py, 1867f16de32Snicm u_int nx, struct overlay_ranges *r) 1873546f4c9Snicm { 188f38784aeSnicm struct menu_data *md = data; 189f38784aeSnicm struct menu *menu = md->menu; 190f38784aeSnicm 1917f16de32Snicm server_client_overlay_range(md->px, md->py, menu->width + 4, 1927f16de32Snicm menu->count + 2, px, py, nx, r); 193f38784aeSnicm } 194f38784aeSnicm 195f38784aeSnicm void 196f38784aeSnicm menu_draw_cb(struct client *c, void *data, 197f38784aeSnicm __unused struct screen_redraw_ctx *rctx) 198f38784aeSnicm { 199f38784aeSnicm struct menu_data *md = data; 2003546f4c9Snicm struct tty *tty = &c->tty; 2013546f4c9Snicm struct screen *s = &md->s; 2023546f4c9Snicm struct menu *menu = md->menu; 2033546f4c9Snicm struct screen_write_ctx ctx; 20408198138Snicm u_int i, px = md->px, py = md->py; 20548a3a827Snicm 20683e83a91Snicm screen_write_start(&ctx, s); 2073546f4c9Snicm screen_write_clearscreen(&ctx, 8); 20817d7ce67Snicm 20917d7ce67Snicm if (md->border_lines != BOX_LINES_NONE) { 21017d7ce67Snicm screen_write_box(&ctx, menu->width + 4, menu->count + 2, 21117d7ce67Snicm md->border_lines, &md->border_style, menu->title); 21217d7ce67Snicm } 21317d7ce67Snicm 21417d7ce67Snicm screen_write_menu(&ctx, menu, md->choice, md->border_lines, 21519c94b00Snicm &md->style, &md->border_style, &md->selected_style); 2163546f4c9Snicm screen_write_stop(&ctx); 2173546f4c9Snicm 21883e83a91Snicm for (i = 0; i < screen_size_y(&md->s); i++) { 21983e83a91Snicm tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i, 22083e83a91Snicm &grid_default_cell, NULL); 22183e83a91Snicm } 2223546f4c9Snicm } 2233546f4c9Snicm 224f38784aeSnicm void 225f38784aeSnicm menu_free_cb(__unused struct client *c, void *data) 2263546f4c9Snicm { 227f38784aeSnicm struct menu_data *md = data; 2283546f4c9Snicm 2293546f4c9Snicm if (md->item != NULL) 230780ca620Snicm cmdq_continue(md->item); 2313546f4c9Snicm 232f43bc87cSnicm if (md->cb != NULL) 233f43bc87cSnicm md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data); 234f43bc87cSnicm 2353546f4c9Snicm screen_free(&md->s); 2363546f4c9Snicm menu_free(md->menu); 2373546f4c9Snicm free(md); 2383546f4c9Snicm } 2393546f4c9Snicm 240f38784aeSnicm int 241f38784aeSnicm menu_key_cb(struct client *c, void *data, struct key_event *event) 2423546f4c9Snicm { 243f38784aeSnicm struct menu_data *md = data; 2443546f4c9Snicm struct menu *menu = md->menu; 2453546f4c9Snicm struct mouse_event *m = &event->m; 2463546f4c9Snicm u_int i; 2473546f4c9Snicm int count = menu->count, old = md->choice; 24810e1651aSnicm const char *name = NULL; 2491c43462cSnicm const struct menu_item *item; 2501c43462cSnicm struct cmdq_state *state; 2511c43462cSnicm enum cmd_parse_status status; 2521c43462cSnicm char *error; 2533546f4c9Snicm 2543546f4c9Snicm if (KEYC_IS_MOUSE(event->key)) { 25545f44af0Snicm if (md->flags & MENU_NOMOUSE) { 2564e5846a2Snicm if (MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1) 25745f44af0Snicm return (1); 2583546f4c9Snicm return (0); 25945f44af0Snicm } 2603546f4c9Snicm if (m->x < md->px || 2613546f4c9Snicm m->x > md->px + 4 + menu->width || 2623546f4c9Snicm m->y < md->py + 1 || 2633546f4c9Snicm m->y > md->py + 1 + count - 1) { 2645971ac85Snicm if (~md->flags & MENU_STAYOPEN) { 2653546f4c9Snicm if (MOUSE_RELEASE(m->b)) 2663546f4c9Snicm return (1); 2675971ac85Snicm } else { 2685971ac85Snicm if (!MOUSE_RELEASE(m->b) && 2694e5846a2Snicm !MOUSE_WHEEL(m->b) && 2705971ac85Snicm !MOUSE_DRAG(m->b)) 2715971ac85Snicm return (1); 2725971ac85Snicm } 2733546f4c9Snicm if (md->choice != -1) { 2743546f4c9Snicm md->choice = -1; 2753546f4c9Snicm c->flags |= CLIENT_REDRAWOVERLAY; 2763546f4c9Snicm } 2773546f4c9Snicm return (0); 2783546f4c9Snicm } 2795971ac85Snicm if (~md->flags & MENU_STAYOPEN) { 2803546f4c9Snicm if (MOUSE_RELEASE(m->b)) 2813546f4c9Snicm goto chosen; 2825971ac85Snicm } else { 2834e5846a2Snicm if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b)) 2845971ac85Snicm goto chosen; 2855971ac85Snicm } 286245334a3Snicm md->choice = m->y - (md->py + 1); 2873546f4c9Snicm if (md->choice != old) 2883546f4c9Snicm c->flags |= CLIENT_REDRAWOVERLAY; 2893546f4c9Snicm return (0); 2903546f4c9Snicm } 291bf2321beSnicm for (i = 0; i < (u_int)count; i++) { 292bf2321beSnicm name = menu->items[i].name; 293bf2321beSnicm if (name == NULL || *name == '-') 294bf2321beSnicm continue; 295bf2321beSnicm if (event->key == menu->items[i].key) { 296bf2321beSnicm md->choice = i; 297bf2321beSnicm goto chosen; 298bf2321beSnicm } 299bf2321beSnicm } 3005416581eSnicm switch (event->key & ~KEYC_MASK_FLAGS) { 3013546f4c9Snicm case KEYC_UP: 302bf2321beSnicm case 'k': 303f05a5af3Snicm if (old == -1) 304f05a5af3Snicm old = 0; 3053546f4c9Snicm do { 3063546f4c9Snicm if (md->choice == -1 || md->choice == 0) 3073546f4c9Snicm md->choice = count - 1; 3083546f4c9Snicm else 3093546f4c9Snicm md->choice--; 310f05a5af3Snicm name = menu->items[md->choice].name; 311f05a5af3Snicm } while ((name == NULL || *name == '-') && md->choice != old); 3123546f4c9Snicm c->flags |= CLIENT_REDRAWOVERLAY; 3133546f4c9Snicm return (0); 314d6317402Snicm case KEYC_BSPACE: 315d6317402Snicm if (~md->flags & MENU_TAB) 316d6317402Snicm break; 317d6317402Snicm return (1); 318d6317402Snicm case '\011': /* Tab */ 319d6317402Snicm if (~md->flags & MENU_TAB) 320d6317402Snicm break; 321d6317402Snicm if (md->choice == count - 1) 322d6317402Snicm return (1); 323d6317402Snicm /* FALLTHROUGH */ 3243546f4c9Snicm case KEYC_DOWN: 325bf2321beSnicm case 'j': 326f05a5af3Snicm if (old == -1) 327f05a5af3Snicm old = 0; 3283546f4c9Snicm do { 3293546f4c9Snicm if (md->choice == -1 || md->choice == count - 1) 3303546f4c9Snicm md->choice = 0; 3313546f4c9Snicm else 3323546f4c9Snicm md->choice++; 333f05a5af3Snicm name = menu->items[md->choice].name; 334f05a5af3Snicm } while ((name == NULL || *name == '-') && md->choice != old); 3353546f4c9Snicm c->flags |= CLIENT_REDRAWOVERLAY; 3363546f4c9Snicm return (0); 337d6317402Snicm case KEYC_PPAGE: 338719f5715Snicm case 'b'|KEYC_CTRL: 33903a39b52Snicm if (md->choice < 6) 340d6317402Snicm md->choice = 0; 34103a39b52Snicm else { 34203a39b52Snicm i = 5; 34303a39b52Snicm while (i > 0) { 34403a39b52Snicm md->choice--; 34503a39b52Snicm name = menu->items[md->choice].name; 34603a39b52Snicm if (md->choice != 0 && 34703a39b52Snicm (name != NULL && *name != '-')) 34803a39b52Snicm i--; 34903a39b52Snicm else if (md->choice == 0) 35003a39b52Snicm break; 35103a39b52Snicm } 35203a39b52Snicm } 35303a39b52Snicm c->flags |= CLIENT_REDRAWOVERLAY; 35403a39b52Snicm break; 35503a39b52Snicm case KEYC_NPAGE: 35603a39b52Snicm if (md->choice > count - 6) { 35703a39b52Snicm md->choice = count - 1; 35803a39b52Snicm name = menu->items[md->choice].name; 35903a39b52Snicm } else { 36003a39b52Snicm i = 5; 36103a39b52Snicm while (i > 0) { 362d6317402Snicm md->choice++; 36303a39b52Snicm name = menu->items[md->choice].name; 36403a39b52Snicm if (md->choice != count - 1 && 36503a39b52Snicm (name != NULL && *name != '-')) 36603a39b52Snicm i++; 36703a39b52Snicm else if (md->choice == count - 1) 36803a39b52Snicm break; 36903a39b52Snicm } 37003a39b52Snicm } 37103a39b52Snicm while (name == NULL || *name == '-') { 37203a39b52Snicm md->choice--; 37303a39b52Snicm name = menu->items[md->choice].name; 37403a39b52Snicm } 37503a39b52Snicm c->flags |= CLIENT_REDRAWOVERLAY; 37603a39b52Snicm break; 37703a39b52Snicm case 'g': 37803a39b52Snicm case KEYC_HOME: 37903a39b52Snicm md->choice = 0; 38003a39b52Snicm name = menu->items[md->choice].name; 38103a39b52Snicm while (name == NULL || *name == '-') { 38203a39b52Snicm md->choice++; 38303a39b52Snicm name = menu->items[md->choice].name; 38403a39b52Snicm } 385d6317402Snicm c->flags |= CLIENT_REDRAWOVERLAY; 386d6317402Snicm break; 387d6317402Snicm case 'G': 38803a39b52Snicm case KEYC_END: 389d6317402Snicm md->choice = count - 1; 39003a39b52Snicm name = menu->items[md->choice].name; 39103a39b52Snicm while (name == NULL || *name == '-') { 392d6317402Snicm md->choice--; 39303a39b52Snicm name = menu->items[md->choice].name; 39403a39b52Snicm } 395d6317402Snicm c->flags |= CLIENT_REDRAWOVERLAY; 396d6317402Snicm break; 397719f5715Snicm case 'f'|KEYC_CTRL: 398d6317402Snicm break; 3993546f4c9Snicm case '\r': 4003546f4c9Snicm goto chosen; 4013546f4c9Snicm case '\033': /* Escape */ 402719f5715Snicm case 'c'|KEYC_CTRL: 403719f5715Snicm case 'g'|KEYC_CTRL: 4043546f4c9Snicm case 'q': 4053546f4c9Snicm return (1); 4063546f4c9Snicm } 4073546f4c9Snicm return (0); 4083546f4c9Snicm 4093546f4c9Snicm chosen: 4103546f4c9Snicm if (md->choice == -1) 4113546f4c9Snicm return (1); 4123546f4c9Snicm item = &menu->items[md->choice]; 413e579339dSnicm if (item->name == NULL || *item->name == '-') { 414e579339dSnicm if (md->flags & MENU_STAYOPEN) 4155971ac85Snicm return (0); 4163546f4c9Snicm return (1); 417e579339dSnicm } 4183546f4c9Snicm if (md->cb != NULL) { 4193546f4c9Snicm md->cb(md->menu, md->choice, item->key, md->data); 420f43bc87cSnicm md->cb = NULL; 4213546f4c9Snicm return (1); 4223546f4c9Snicm } 423df6ab229Snicm 4247db4c597Snicm if (md->item != NULL) 425823b6d6dSnicm event = cmdq_get_event(md->item); 4267db4c597Snicm else 427823b6d6dSnicm event = NULL; 4281c43462cSnicm state = cmdq_new_state(&md->fs, event, 0); 4291c43462cSnicm 4301c43462cSnicm status = cmd_parse_and_append(item->command, NULL, c, state, &error); 4311c43462cSnicm if (status == CMD_PARSE_ERROR) { 4321c43462cSnicm cmdq_append(c, cmdq_get_error(error)); 4331c43462cSnicm free(error); 4343546f4c9Snicm } 4351c43462cSnicm cmdq_free_state(state); 4361c43462cSnicm 4373546f4c9Snicm return (1); 4383546f4c9Snicm } 4393546f4c9Snicm 44019c94b00Snicm static void 44119c94b00Snicm menu_set_style(struct client *c, struct grid_cell *gc, const char *style, 44219c94b00Snicm const char *option) 44319c94b00Snicm { 44419c94b00Snicm struct style sytmp; 44519c94b00Snicm struct options *o = c->session->curw->window->options; 44619c94b00Snicm 44719c94b00Snicm memcpy(gc, &grid_default_cell, sizeof *gc); 44819c94b00Snicm style_apply(gc, o, option, NULL); 44919c94b00Snicm if (style != NULL) { 45019c94b00Snicm style_set(&sytmp, &grid_default_cell); 45119c94b00Snicm if (style_parse(&sytmp, gc, style) == 0) { 45219c94b00Snicm gc->fg = sytmp.gc.fg; 45319c94b00Snicm gc->bg = sytmp.gc.bg; 45419c94b00Snicm } 45519c94b00Snicm } 45619c94b00Snicm } 45719c94b00Snicm 458f38784aeSnicm struct menu_data * 459009dd187Snicm menu_prepare(struct menu *menu, int flags, int starting_choice, 460009dd187Snicm struct cmdq_item *item, u_int px, u_int py, struct client *c, 46119c94b00Snicm enum box_lines lines, const char *style, const char *selected_style, 46219c94b00Snicm const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb, 46319c94b00Snicm void *data) 4643546f4c9Snicm { 4653546f4c9Snicm struct menu_data *md; 466009dd187Snicm int choice; 467b6543d77Snicm const char *name; 46817d7ce67Snicm struct options *o = c->session->curw->window->options; 4693546f4c9Snicm 4703546f4c9Snicm if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) 471f38784aeSnicm return (NULL); 472e38aac43Snicm if (px + menu->width + 4 > c->tty.sx) 473e38aac43Snicm px = c->tty.sx - menu->width - 4; 474e38aac43Snicm if (py + menu->count + 2 > c->tty.sy) 475e38aac43Snicm py = c->tty.sy - menu->count - 2; 4763546f4c9Snicm 47717d7ce67Snicm if (lines == BOX_LINES_DEFAULT) 47817d7ce67Snicm lines = options_get_number(o, "menu-border-lines"); 47917d7ce67Snicm 4803546f4c9Snicm md = xcalloc(1, sizeof *md); 4813546f4c9Snicm md->item = item; 4823546f4c9Snicm md->flags = flags; 48317d7ce67Snicm md->border_lines = lines; 48417d7ce67Snicm 48519c94b00Snicm menu_set_style(c, &md->style, style, "menu-style"); 48619c94b00Snicm menu_set_style(c, &md->selected_style, selected_style, 48719c94b00Snicm "menu-selected-style"); 48819c94b00Snicm menu_set_style(c, &md->border_style, border_style, "menu-border-style"); 4893546f4c9Snicm 490f43bc87cSnicm if (fs != NULL) 4913546f4c9Snicm cmd_find_copy_state(&md->fs, fs); 4923546f4c9Snicm screen_init(&md->s, menu->width + 4, menu->count + 2, 0); 49383e83a91Snicm if (~md->flags & MENU_NOMOUSE) 4942b7bea19Snicm md->s.mode |= (MODE_MOUSE_ALL|MODE_MOUSE_BUTTON); 495e620b8e2Snicm md->s.mode &= ~MODE_CURSOR; 4963546f4c9Snicm 4973546f4c9Snicm md->px = px; 4983546f4c9Snicm md->py = py; 4993546f4c9Snicm 5003546f4c9Snicm md->menu = menu; 501009dd187Snicm md->choice = -1; 502009dd187Snicm 503b6543d77Snicm if (md->flags & MENU_NOMOUSE) { 504009dd187Snicm if (starting_choice >= (int)menu->count) { 505009dd187Snicm starting_choice = menu->count - 1; 506009dd187Snicm choice = starting_choice + 1; 507009dd187Snicm for (;;) { 508009dd187Snicm name = menu->items[choice - 1].name; 509009dd187Snicm if (name != NULL && *name != '-') { 510009dd187Snicm md->choice = choice - 1; 511b6543d77Snicm break; 512b6543d77Snicm } 513009dd187Snicm if (--choice == 0) 514009dd187Snicm choice = menu->count; 515009dd187Snicm if (choice == starting_choice + 1) 516009dd187Snicm break; 517009dd187Snicm } 518009dd187Snicm } else if (starting_choice >= 0) { 519009dd187Snicm choice = starting_choice; 520009dd187Snicm for (;;) { 521009dd187Snicm name = menu->items[choice].name; 522009dd187Snicm if (name != NULL && *name != '-') { 523009dd187Snicm md->choice = choice; 524009dd187Snicm break; 525009dd187Snicm } 526009dd187Snicm if (++choice == (int)menu->count) 527009dd187Snicm choice = 0; 528009dd187Snicm if (choice == starting_choice) 529009dd187Snicm break; 530009dd187Snicm } 531009dd187Snicm } 532009dd187Snicm } 5333546f4c9Snicm 5343546f4c9Snicm md->cb = cb; 5353546f4c9Snicm md->data = data; 536f38784aeSnicm return (md); 537f38784aeSnicm } 5383546f4c9Snicm 539f38784aeSnicm int 540009dd187Snicm menu_display(struct menu *menu, int flags, int starting_choice, 541009dd187Snicm struct cmdq_item *item, u_int px, u_int py, struct client *c, 54219c94b00Snicm enum box_lines lines, const char *style, const char *selected_style, 54319c94b00Snicm const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb, 54419c94b00Snicm void *data) 545f38784aeSnicm { 546f38784aeSnicm struct menu_data *md; 547f38784aeSnicm 54817d7ce67Snicm md = menu_prepare(menu, flags, starting_choice, item, px, py, c, lines, 54919c94b00Snicm style, selected_style, border_style, fs, cb, data); 550f38784aeSnicm if (md == NULL) 551f38784aeSnicm return (-1); 5520cac3d2dSnicm server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, 553c0287628Snicm menu_key_cb, menu_free_cb, NULL, md); 5543546f4c9Snicm return (0); 5553546f4c9Snicm } 556