Line | Count | Source |
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 | | char *style; |
31 | | char *border_style; |
32 | | char *selected_style; |
33 | | |
34 | | struct grid_cell style_gc; |
35 | | struct grid_cell border_style_gc; |
36 | | struct grid_cell selected_style_gc; |
37 | | enum box_lines border_lines; |
38 | | |
39 | | struct cmd_find_state fs; |
40 | | struct screen s; |
41 | | struct visible_ranges r; |
42 | | |
43 | | u_int px; |
44 | | u_int py; |
45 | | |
46 | | struct menu *menu; |
47 | | int choice; |
48 | | |
49 | | menu_choice_cb cb; |
50 | | void *data; |
51 | | }; |
52 | | |
53 | | void |
54 | | menu_add_items(struct menu *menu, const struct menu_item *items, |
55 | | struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) |
56 | 0 | { |
57 | 0 | const struct menu_item *loop; |
58 | |
|
59 | 0 | for (loop = items; loop->name != NULL; loop++) |
60 | 0 | menu_add_item(menu, loop, qitem, c, fs); |
61 | 0 | } |
62 | | |
63 | | void |
64 | | menu_add_item(struct menu *menu, const struct menu_item *item, |
65 | | struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) |
66 | 0 | { |
67 | 0 | struct menu_item *new_item; |
68 | 0 | const char *key = NULL, *cmd, *suffix = ""; |
69 | 0 | char *s, *trimmed, *name; |
70 | 0 | u_int width, max_width; |
71 | 0 | int line; |
72 | 0 | size_t keylen, slen; |
73 | |
|
74 | 0 | line = (item == NULL || item->name == NULL || *item->name == '\0'); |
75 | 0 | if (line && menu->count == 0) |
76 | 0 | return; |
77 | 0 | if (line && menu->items[menu->count - 1].name == NULL) |
78 | 0 | return; |
79 | | |
80 | 0 | menu->items = xreallocarray(menu->items, menu->count + 1, |
81 | 0 | sizeof *menu->items); |
82 | 0 | new_item = &menu->items[menu->count++]; |
83 | 0 | memset(new_item, 0, sizeof *new_item); |
84 | |
|
85 | 0 | if (line) |
86 | 0 | return; |
87 | | |
88 | 0 | if (fs != NULL) |
89 | 0 | s = format_single_from_state(qitem, item->name, c, fs); |
90 | 0 | else |
91 | 0 | s = format_single(qitem, item->name, c, NULL, NULL, NULL); |
92 | 0 | if (*s == '\0') { /* no item if empty after format expanded */ |
93 | 0 | free(s); |
94 | 0 | menu->count--; |
95 | 0 | return; |
96 | 0 | } |
97 | 0 | max_width = c->tty.sx - 4; |
98 | |
|
99 | 0 | slen = strlen(s); |
100 | 0 | if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) { |
101 | 0 | key = key_string_lookup_key(item->key, 0); |
102 | 0 | keylen = strlen(key) + 3; /* 3 = space and two brackets */ |
103 | | |
104 | | /* |
105 | | * Add the key if it is shorter than a quarter of the available |
106 | | * space or there is space for the entire item text and the |
107 | | * key. |
108 | | */ |
109 | 0 | if (keylen <= max_width / 4) |
110 | 0 | max_width -= keylen; |
111 | 0 | else if (keylen >= max_width || slen >= max_width - keylen) |
112 | 0 | key = NULL; |
113 | 0 | } |
114 | |
|
115 | 0 | if (slen > max_width) { |
116 | 0 | max_width--; |
117 | 0 | suffix = ">"; |
118 | 0 | } |
119 | 0 | trimmed = format_trim_right(s, max_width); |
120 | 0 | if (key != NULL) { |
121 | 0 | xasprintf(&name, "%s%s#[default] #[align=right](%s)", |
122 | 0 | trimmed, suffix, key); |
123 | 0 | } else |
124 | 0 | xasprintf(&name, "%s%s", trimmed, suffix); |
125 | 0 | free(trimmed); |
126 | |
|
127 | 0 | new_item->name = name; |
128 | 0 | free(s); |
129 | |
|
130 | 0 | cmd = item->command; |
131 | 0 | if (cmd != NULL) { |
132 | 0 | if (fs != NULL) |
133 | 0 | s = format_single_from_state(qitem, cmd, c, fs); |
134 | 0 | else |
135 | 0 | s = format_single(qitem, cmd, c, NULL, NULL, NULL); |
136 | 0 | } else |
137 | 0 | s = NULL; |
138 | 0 | new_item->command = s; |
139 | 0 | new_item->key = item->key; |
140 | |
|
141 | 0 | width = format_width(new_item->name); |
142 | 0 | if (*new_item->name == '-') |
143 | 0 | width--; |
144 | 0 | if (width > menu->width) |
145 | 0 | menu->width = width; |
146 | 0 | } |
147 | | |
148 | | struct menu * |
149 | | menu_create(const char *title) |
150 | 0 | { |
151 | 0 | struct menu *menu; |
152 | |
|
153 | 0 | menu = xcalloc(1, sizeof *menu); |
154 | 0 | menu->title = xstrdup(title); |
155 | 0 | menu->width = format_width(title); |
156 | |
|
157 | 0 | return (menu); |
158 | 0 | } |
159 | | |
160 | | void |
161 | | menu_free(struct menu *menu) |
162 | 0 | { |
163 | 0 | u_int i; |
164 | |
|
165 | 0 | if (menu == NULL) |
166 | 0 | return; |
167 | | |
168 | 0 | for (i = 0; i < menu->count; i++) { |
169 | 0 | free((void *)menu->items[i].name); |
170 | 0 | free((void *)menu->items[i].command); |
171 | 0 | } |
172 | 0 | free(menu->items); |
173 | |
|
174 | 0 | free((void *)menu->title); |
175 | 0 | free(menu); |
176 | 0 | } |
177 | | |
178 | | struct screen * |
179 | | menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy) |
180 | 0 | { |
181 | 0 | struct menu_data *md = data; |
182 | |
|
183 | 0 | *cx = md->px + 2; |
184 | 0 | if (md->choice == -1) |
185 | 0 | *cy = md->py; |
186 | 0 | else |
187 | 0 | *cy = md->py + 1 + md->choice; |
188 | |
|
189 | 0 | return (&md->s); |
190 | 0 | } |
191 | | |
192 | | /* Return parts of the input range which are not obstructed by the menu. */ |
193 | | struct visible_ranges * |
194 | | menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py, |
195 | | u_int nx) |
196 | 0 | { |
197 | 0 | struct menu_data *md = data; |
198 | 0 | struct menu *menu = md->menu; |
199 | |
|
200 | 0 | server_client_overlay_range(md->px, md->py, menu->width + 4, |
201 | 0 | menu->count + 2, px, py, nx, &md->r); |
202 | 0 | return (&md->r); |
203 | 0 | } |
204 | | |
205 | | static void |
206 | | menu_reapply_styles(struct menu_data *md, struct client *c) |
207 | 0 | { |
208 | 0 | struct session *s = c->session; |
209 | 0 | struct options *o; |
210 | 0 | struct format_tree *ft; |
211 | 0 | struct style sytmp; |
212 | |
|
213 | 0 | if (s == NULL) |
214 | 0 | return; |
215 | 0 | o = s->curw->window->options; |
216 | |
|
217 | 0 | ft = format_create_defaults(NULL, c, s, s->curw, NULL); |
218 | | |
219 | | /* Reapply menu style from options. */ |
220 | 0 | memcpy(&md->style_gc, &grid_default_cell, sizeof md->style_gc); |
221 | 0 | style_apply(&md->style_gc, o, "menu-style", ft); |
222 | 0 | if (md->style != NULL) { |
223 | 0 | style_set(&sytmp, &grid_default_cell); |
224 | 0 | if (style_parse(&sytmp, &md->style_gc, md->style) == 0) { |
225 | 0 | md->style_gc.fg = sytmp.gc.fg; |
226 | 0 | md->style_gc.bg = sytmp.gc.bg; |
227 | 0 | } |
228 | 0 | } |
229 | | |
230 | | /* Reapply selected style from options. */ |
231 | 0 | memcpy(&md->selected_style_gc, &grid_default_cell, |
232 | 0 | sizeof md->selected_style_gc); |
233 | 0 | style_apply(&md->selected_style_gc, o, "menu-selected-style", ft); |
234 | 0 | if (md->selected_style != NULL) { |
235 | 0 | style_set(&sytmp, &grid_default_cell); |
236 | 0 | if (style_parse(&sytmp, &md->selected_style_gc, |
237 | 0 | md->selected_style) == 0) { |
238 | 0 | md->selected_style_gc.fg = sytmp.gc.fg; |
239 | 0 | md->selected_style_gc.bg = sytmp.gc.bg; |
240 | 0 | } |
241 | 0 | } |
242 | | |
243 | | /* Reapply border style from options. */ |
244 | 0 | memcpy(&md->border_style_gc, &grid_default_cell, |
245 | 0 | sizeof md->border_style_gc); |
246 | 0 | style_apply(&md->border_style_gc, o, "menu-border-style", ft); |
247 | 0 | if (md->border_style != NULL) { |
248 | 0 | style_set(&sytmp, &grid_default_cell); |
249 | 0 | if (style_parse(&sytmp, &md->border_style_gc, |
250 | 0 | md->border_style) == 0) { |
251 | 0 | md->border_style_gc.fg = sytmp.gc.fg; |
252 | 0 | md->border_style_gc.bg = sytmp.gc.bg; |
253 | 0 | } |
254 | 0 | } |
255 | |
|
256 | 0 | format_free(ft); |
257 | 0 | } |
258 | | |
259 | | void |
260 | | menu_draw_cb(struct client *c, void *data, |
261 | | __unused struct screen_redraw_ctx *rctx) |
262 | 0 | { |
263 | 0 | struct menu_data *md = data; |
264 | 0 | struct tty *tty = &c->tty; |
265 | 0 | struct screen *s = &md->s; |
266 | 0 | struct menu *menu = md->menu; |
267 | 0 | struct screen_write_ctx ctx; |
268 | 0 | u_int i, px = md->px, py = md->py; |
269 | |
|
270 | 0 | menu_reapply_styles(md, c); |
271 | |
|
272 | 0 | screen_write_start(&ctx, s); |
273 | 0 | screen_write_clearscreen(&ctx, 8); |
274 | |
|
275 | 0 | if (md->border_lines != BOX_LINES_NONE) { |
276 | 0 | screen_write_box(&ctx, menu->width + 4, menu->count + 2, |
277 | 0 | md->border_lines, &md->border_style_gc, menu->title); |
278 | 0 | } |
279 | |
|
280 | 0 | screen_write_menu(&ctx, menu, md->choice, md->border_lines, |
281 | 0 | &md->style_gc, &md->border_style_gc, &md->selected_style_gc); |
282 | 0 | screen_write_stop(&ctx); |
283 | |
|
284 | 0 | for (i = 0; i < screen_size_y(&md->s); i++) { |
285 | 0 | tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i, |
286 | 0 | &grid_default_cell, NULL); |
287 | 0 | } |
288 | 0 | } |
289 | | |
290 | | void |
291 | | menu_free_cb(__unused struct client *c, void *data) |
292 | 0 | { |
293 | 0 | struct menu_data *md = data; |
294 | |
|
295 | 0 | if (md->item != NULL) |
296 | 0 | cmdq_continue(md->item); |
297 | |
|
298 | 0 | if (md->cb != NULL) |
299 | 0 | md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data); |
300 | |
|
301 | 0 | free(md->r.ranges); |
302 | 0 | screen_free(&md->s); |
303 | |
|
304 | 0 | menu_free(md->menu); |
305 | 0 | free(md->style); |
306 | 0 | free(md->selected_style); |
307 | 0 | free(md->border_style); |
308 | 0 | free(md); |
309 | 0 | } |
310 | | |
311 | | int |
312 | | menu_key_cb(struct client *c, void *data, struct key_event *event) |
313 | 0 | { |
314 | 0 | struct menu_data *md = data; |
315 | 0 | struct menu *menu = md->menu; |
316 | 0 | struct mouse_event *m = &event->m; |
317 | 0 | u_int i; |
318 | 0 | int count = menu->count, old = md->choice; |
319 | 0 | const char *name = NULL; |
320 | 0 | const struct menu_item *item; |
321 | 0 | struct cmdq_state *state; |
322 | 0 | enum cmd_parse_status status; |
323 | 0 | char *error; |
324 | |
|
325 | 0 | if (KEYC_IS_MOUSE(event->key)) { |
326 | 0 | if (md->flags & MENU_NOMOUSE) { |
327 | 0 | if (MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1) |
328 | 0 | return (1); |
329 | 0 | return (0); |
330 | 0 | } |
331 | 0 | if (m->x < md->px || |
332 | 0 | m->x > md->px + 4 + menu->width || |
333 | 0 | m->y < md->py + 1 || |
334 | 0 | m->y > md->py + 1 + count - 1) { |
335 | 0 | if (~md->flags & MENU_STAYOPEN) { |
336 | 0 | if (MOUSE_RELEASE(m->b)) |
337 | 0 | return (1); |
338 | 0 | } else { |
339 | 0 | if (!MOUSE_RELEASE(m->b) && |
340 | 0 | !MOUSE_WHEEL(m->b) && |
341 | 0 | !MOUSE_DRAG(m->b)) |
342 | 0 | return (1); |
343 | 0 | } |
344 | 0 | if (md->choice != -1) { |
345 | 0 | md->choice = -1; |
346 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
347 | 0 | } |
348 | 0 | return (0); |
349 | 0 | } |
350 | 0 | if (~md->flags & MENU_STAYOPEN) { |
351 | 0 | if (MOUSE_RELEASE(m->b)) |
352 | 0 | goto chosen; |
353 | 0 | } else { |
354 | 0 | if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b)) |
355 | 0 | goto chosen; |
356 | 0 | } |
357 | 0 | md->choice = m->y - (md->py + 1); |
358 | 0 | if (md->choice != old) |
359 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
360 | 0 | return (0); |
361 | 0 | } |
362 | 0 | for (i = 0; i < (u_int)count; i++) { |
363 | 0 | name = menu->items[i].name; |
364 | 0 | if (name == NULL || *name == '-') |
365 | 0 | continue; |
366 | 0 | if ((event->key & ~KEYC_MASK_FLAGS) == menu->items[i].key) { |
367 | 0 | md->choice = i; |
368 | 0 | goto chosen; |
369 | 0 | } |
370 | 0 | } |
371 | 0 | switch (event->key & ~KEYC_MASK_FLAGS) { |
372 | 0 | case KEYC_BTAB: |
373 | 0 | case KEYC_UP: |
374 | 0 | case 'k': |
375 | 0 | if (old == -1) |
376 | 0 | old = 0; |
377 | 0 | do { |
378 | 0 | if (md->choice == -1 || md->choice == 0) |
379 | 0 | md->choice = count - 1; |
380 | 0 | else |
381 | 0 | md->choice--; |
382 | 0 | name = menu->items[md->choice].name; |
383 | 0 | } while ((name == NULL || *name == '-') && md->choice != old); |
384 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
385 | 0 | return (0); |
386 | 0 | case KEYC_BSPACE: |
387 | 0 | if (~md->flags & MENU_TAB) |
388 | 0 | break; |
389 | 0 | return (1); |
390 | 0 | case '\011': /* Tab */ |
391 | 0 | if (~md->flags & MENU_TAB) |
392 | 0 | break; |
393 | 0 | if (md->choice == count - 1) |
394 | 0 | return (1); |
395 | | /* FALLTHROUGH */ |
396 | 0 | case KEYC_DOWN: |
397 | 0 | case 'j': |
398 | 0 | if (old == -1) |
399 | 0 | old = 0; |
400 | 0 | do { |
401 | 0 | if (md->choice == -1 || md->choice == count - 1) |
402 | 0 | md->choice = 0; |
403 | 0 | else |
404 | 0 | md->choice++; |
405 | 0 | name = menu->items[md->choice].name; |
406 | 0 | } while ((name == NULL || *name == '-') && md->choice != old); |
407 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
408 | 0 | return (0); |
409 | 0 | case KEYC_PPAGE: |
410 | 0 | case 'b'|KEYC_CTRL: |
411 | 0 | if (md->choice < 6) |
412 | 0 | md->choice = 0; |
413 | 0 | else { |
414 | 0 | i = 5; |
415 | 0 | while (i > 0) { |
416 | 0 | md->choice--; |
417 | 0 | name = menu->items[md->choice].name; |
418 | 0 | if (md->choice != 0 && |
419 | 0 | (name != NULL && *name != '-')) |
420 | 0 | i--; |
421 | 0 | else if (md->choice == 0) |
422 | 0 | break; |
423 | 0 | } |
424 | 0 | } |
425 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
426 | 0 | break; |
427 | 0 | case KEYC_NPAGE: |
428 | 0 | if (md->choice > count - 6) { |
429 | 0 | md->choice = count - 1; |
430 | 0 | name = menu->items[md->choice].name; |
431 | 0 | } else { |
432 | 0 | i = 5; |
433 | 0 | while (i > 0) { |
434 | 0 | md->choice++; |
435 | 0 | name = menu->items[md->choice].name; |
436 | 0 | if (md->choice != count - 1 && |
437 | 0 | (name != NULL && *name != '-')) |
438 | 0 | i--; |
439 | 0 | else if (md->choice == count - 1) |
440 | 0 | break; |
441 | 0 | } |
442 | 0 | } |
443 | 0 | while (name == NULL || *name == '-') { |
444 | 0 | md->choice--; |
445 | 0 | name = menu->items[md->choice].name; |
446 | 0 | } |
447 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
448 | 0 | break; |
449 | 0 | case 'g': |
450 | 0 | case KEYC_HOME: |
451 | 0 | md->choice = 0; |
452 | 0 | name = menu->items[md->choice].name; |
453 | 0 | while (name == NULL || *name == '-') { |
454 | 0 | md->choice++; |
455 | 0 | name = menu->items[md->choice].name; |
456 | 0 | } |
457 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
458 | 0 | break; |
459 | 0 | case 'G': |
460 | 0 | case KEYC_END: |
461 | 0 | md->choice = count - 1; |
462 | 0 | name = menu->items[md->choice].name; |
463 | 0 | while (name == NULL || *name == '-') { |
464 | 0 | md->choice--; |
465 | 0 | name = menu->items[md->choice].name; |
466 | 0 | } |
467 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
468 | 0 | break; |
469 | 0 | case 'f'|KEYC_CTRL: |
470 | 0 | break; |
471 | 0 | case '\r': |
472 | 0 | goto chosen; |
473 | 0 | case '\033': /* Escape */ |
474 | 0 | case 'c'|KEYC_CTRL: |
475 | 0 | case 'g'|KEYC_CTRL: |
476 | 0 | case 'q': |
477 | 0 | return (1); |
478 | 0 | } |
479 | 0 | return (0); |
480 | | |
481 | 0 | chosen: |
482 | 0 | if (md->choice == -1) |
483 | 0 | return (1); |
484 | 0 | item = &menu->items[md->choice]; |
485 | 0 | if (item->name == NULL || *item->name == '-') { |
486 | 0 | if (md->flags & MENU_STAYOPEN) |
487 | 0 | return (0); |
488 | 0 | return (1); |
489 | 0 | } |
490 | 0 | if (md->cb != NULL) { |
491 | 0 | md->cb(md->menu, md->choice, item->key, md->data); |
492 | 0 | md->cb = NULL; |
493 | 0 | return (1); |
494 | 0 | } |
495 | | |
496 | 0 | if (md->item != NULL) |
497 | 0 | event = cmdq_get_event(md->item); |
498 | 0 | else |
499 | 0 | event = NULL; |
500 | 0 | state = cmdq_new_state(&md->fs, event, 0); |
501 | |
|
502 | 0 | status = cmd_parse_and_append(item->command, NULL, c, state, &error); |
503 | 0 | if (status == CMD_PARSE_ERROR) { |
504 | 0 | cmdq_append(c, cmdq_get_error(error)); |
505 | 0 | free(error); |
506 | 0 | } |
507 | 0 | cmdq_free_state(state); |
508 | |
|
509 | 0 | return (1); |
510 | 0 | } |
511 | | |
512 | | static void |
513 | | menu_resize_cb(struct client *c, void *data) |
514 | 0 | { |
515 | 0 | struct menu_data *md = data; |
516 | 0 | u_int nx, ny, w, h; |
517 | |
|
518 | 0 | if (md == NULL) |
519 | 0 | return; |
520 | | |
521 | 0 | nx = md->px; |
522 | 0 | ny = md->py; |
523 | |
|
524 | 0 | w = md->menu->width + 4; |
525 | 0 | h = md->menu->count + 2; |
526 | |
|
527 | 0 | if (nx + w > c->tty.sx) { |
528 | 0 | if (c->tty.sx <= w) |
529 | 0 | nx = 0; |
530 | 0 | else |
531 | 0 | nx = c->tty.sx - w; |
532 | 0 | } |
533 | |
|
534 | 0 | if (ny + h > c->tty.sy) { |
535 | 0 | if (c->tty.sy <= h) |
536 | 0 | ny = 0; |
537 | 0 | else |
538 | 0 | ny = c->tty.sy - h; |
539 | 0 | } |
540 | 0 | md->px = nx; |
541 | 0 | md->py = ny; |
542 | 0 | } |
543 | | |
544 | | struct menu_data * |
545 | | menu_prepare(struct menu *menu, int flags, int starting_choice, |
546 | | struct cmdq_item *item, u_int px, u_int py, struct client *c, |
547 | | enum box_lines lines, const char *style, const char *selected_style, |
548 | | const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb, |
549 | | void *data) |
550 | 0 | { |
551 | 0 | struct menu_data *md; |
552 | 0 | int choice; |
553 | 0 | const char *name; |
554 | 0 | struct options *o = c->session->curw->window->options; |
555 | |
|
556 | 0 | if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) |
557 | 0 | return (NULL); |
558 | 0 | if (px + menu->width + 4 > c->tty.sx) |
559 | 0 | px = c->tty.sx - menu->width - 4; |
560 | 0 | if (py + menu->count + 2 > c->tty.sy) |
561 | 0 | py = c->tty.sy - menu->count - 2; |
562 | |
|
563 | 0 | if (lines == BOX_LINES_DEFAULT) |
564 | 0 | lines = options_get_number(o, "menu-border-lines"); |
565 | |
|
566 | 0 | md = xcalloc(1, sizeof *md); |
567 | 0 | md->item = item; |
568 | 0 | md->flags = flags; |
569 | 0 | md->border_lines = lines; |
570 | |
|
571 | 0 | if (style != NULL) |
572 | 0 | md->style = xstrdup(style); |
573 | 0 | if (selected_style != NULL) |
574 | 0 | md->selected_style = xstrdup(selected_style); |
575 | 0 | if (border_style != NULL) |
576 | 0 | md->border_style = xstrdup(border_style); |
577 | |
|
578 | 0 | if (fs != NULL) |
579 | 0 | cmd_find_copy_state(&md->fs, fs); |
580 | 0 | screen_init(&md->s, menu->width + 4, menu->count + 2, 0); |
581 | 0 | if (~md->flags & MENU_NOMOUSE) |
582 | 0 | md->s.mode |= (MODE_MOUSE_ALL|MODE_MOUSE_BUTTON); |
583 | 0 | md->s.mode &= ~MODE_CURSOR; |
584 | |
|
585 | 0 | md->px = px; |
586 | 0 | md->py = py; |
587 | |
|
588 | 0 | md->menu = menu; |
589 | 0 | md->choice = -1; |
590 | |
|
591 | 0 | if (md->flags & MENU_NOMOUSE) { |
592 | 0 | if (starting_choice >= (int)menu->count) { |
593 | 0 | starting_choice = menu->count - 1; |
594 | 0 | choice = starting_choice + 1; |
595 | 0 | for (;;) { |
596 | 0 | name = menu->items[choice - 1].name; |
597 | 0 | if (name != NULL && *name != '-') { |
598 | 0 | md->choice = choice - 1; |
599 | 0 | break; |
600 | 0 | } |
601 | 0 | if (--choice == 0) |
602 | 0 | choice = menu->count; |
603 | 0 | if (choice == starting_choice + 1) |
604 | 0 | break; |
605 | 0 | } |
606 | 0 | } else if (starting_choice >= 0) { |
607 | 0 | choice = starting_choice; |
608 | 0 | for (;;) { |
609 | 0 | name = menu->items[choice].name; |
610 | 0 | if (name != NULL && *name != '-') { |
611 | 0 | md->choice = choice; |
612 | 0 | break; |
613 | 0 | } |
614 | 0 | if (++choice == (int)menu->count) |
615 | 0 | choice = 0; |
616 | 0 | if (choice == starting_choice) |
617 | 0 | break; |
618 | 0 | } |
619 | 0 | } |
620 | 0 | } |
621 | |
|
622 | 0 | md->cb = cb; |
623 | 0 | md->data = data; |
624 | 0 | return (md); |
625 | 0 | } |
626 | | |
627 | | int |
628 | | menu_display(struct menu *menu, int flags, int starting_choice, |
629 | | struct cmdq_item *item, u_int px, u_int py, struct client *c, |
630 | | enum box_lines lines, const char *style, const char *selected_style, |
631 | | const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb, |
632 | | void *data) |
633 | 0 | { |
634 | 0 | struct menu_data *md; |
635 | |
|
636 | 0 | md = menu_prepare(menu, flags, starting_choice, item, px, py, c, lines, |
637 | 0 | style, selected_style, border_style, fs, cb, data); |
638 | 0 | if (md == NULL) |
639 | 0 | return (-1); |
640 | 0 | server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, |
641 | 0 | menu_key_cb, menu_free_cb, menu_resize_cb, md); |
642 | 0 | return (0); |
643 | 0 | } |