1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2019 Nicholas Marriott <[email protected]> |
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 { |
27 | struct cmdq_item *; |
28 | int ; |
29 | |
30 | struct cmd_find_state ; |
31 | struct screen ; |
32 | |
33 | u_int ; |
34 | u_int ; |
35 | |
36 | struct menu *; |
37 | int ; |
38 | |
39 | menu_choice_cb ; |
40 | void *; |
41 | }; |
42 | |
43 | void |
44 | (struct menu *, const struct menu_item *items, |
45 | struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) |
46 | { |
47 | const struct menu_item *loop; |
48 | |
49 | for (loop = items; loop->name != NULL; loop++) |
50 | menu_add_item(menu, loop, qitem, c, fs); |
51 | } |
52 | |
53 | void |
54 | (struct menu *, const struct menu_item *item, |
55 | struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) |
56 | { |
57 | struct menu_item *new_item; |
58 | const char *key, *cmd; |
59 | char *s, *name; |
60 | u_int width; |
61 | int line; |
62 | |
63 | line = (item == NULL || item->name == NULL || *item->name == '\0'); |
64 | if (line && menu->count == 0) |
65 | return; |
66 | |
67 | menu->items = xreallocarray(menu->items, menu->count + 1, |
68 | sizeof *menu->items); |
69 | new_item = &menu->items[menu->count++]; |
70 | memset(new_item, 0, sizeof *new_item); |
71 | |
72 | if (line) |
73 | return; |
74 | |
75 | if (fs != NULL) |
76 | s = format_single(qitem, item->name, c, fs->s, fs->wl, fs->wp); |
77 | else |
78 | s = format_single(qitem, item->name, c, NULL, NULL, NULL); |
79 | if (*s == '\0') { /* no item if empty after format expanded */ |
80 | menu->count--; |
81 | return; |
82 | } |
83 | if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) { |
84 | key = key_string_lookup_key(item->key); |
85 | xasprintf(&name, "%s#[default] #[align=right](%s)" , s, key); |
86 | } else |
87 | xasprintf(&name, "%s" , s); |
88 | new_item->name = name; |
89 | free(s); |
90 | |
91 | cmd = item->command; |
92 | if (cmd != NULL) { |
93 | if (fs != NULL) |
94 | s = format_single(qitem, cmd, c, fs->s, fs->wl, fs->wp); |
95 | else |
96 | s = format_single(qitem, cmd, c, NULL, NULL, NULL); |
97 | } else |
98 | s = NULL; |
99 | new_item->command = s; |
100 | new_item->key = item->key; |
101 | |
102 | width = format_width(new_item->name); |
103 | if (width > menu->width) |
104 | menu->width = width; |
105 | } |
106 | |
107 | struct menu * |
108 | (const char *title) |
109 | { |
110 | struct menu *; |
111 | |
112 | menu = xcalloc(1, sizeof *menu); |
113 | menu->title = xstrdup(title); |
114 | |
115 | return (menu); |
116 | } |
117 | |
118 | void |
119 | (struct menu *) |
120 | { |
121 | u_int i; |
122 | |
123 | for (i = 0; i < menu->count; i++) { |
124 | free((void *)menu->items[i].name); |
125 | free((void *)menu->items[i].command); |
126 | } |
127 | free(menu->items); |
128 | |
129 | free((void *)menu->title); |
130 | free(menu); |
131 | } |
132 | |
133 | static void |
134 | (struct client *c, __unused struct screen_redraw_ctx *ctx0) |
135 | { |
136 | struct menu_data *md = c->overlay_data; |
137 | struct tty *tty = &c->tty; |
138 | struct screen *s = &md->s; |
139 | struct menu * = md->menu; |
140 | struct screen_write_ctx ctx; |
141 | u_int i, px, py; |
142 | |
143 | screen_write_start(&ctx, NULL, s); |
144 | screen_write_clearscreen(&ctx, 8); |
145 | screen_write_menu(&ctx, menu, md->choice); |
146 | screen_write_stop(&ctx); |
147 | |
148 | px = md->px; |
149 | py = md->py; |
150 | |
151 | for (i = 0; i < screen_size_y(&md->s); i++) |
152 | tty_draw_line(tty, NULL, s, 0, i, menu->width + 4, px, py + i); |
153 | |
154 | if (~md->flags & MENU_NOMOUSE) |
155 | tty_update_mode(tty, MODE_MOUSE_ALL, NULL); |
156 | } |
157 | |
158 | static void |
159 | (struct client *c) |
160 | { |
161 | struct menu_data *md = c->overlay_data; |
162 | |
163 | if (md->item != NULL) |
164 | cmdq_continue(md->item); |
165 | |
166 | if (md->cb != NULL) |
167 | md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data); |
168 | |
169 | screen_free(&md->s); |
170 | menu_free(md->menu); |
171 | free(md); |
172 | } |
173 | |
174 | static int |
175 | (struct client *c, struct key_event *event) |
176 | { |
177 | struct menu_data *md = c->overlay_data; |
178 | struct menu * = md->menu; |
179 | struct mouse_event *m = &event->m; |
180 | u_int i; |
181 | int count = menu->count, old = md->choice; |
182 | const struct menu_item *item; |
183 | struct cmdq_item *new_item; |
184 | struct cmd_parse_result *pr; |
185 | const char *name; |
186 | |
187 | if (KEYC_IS_MOUSE(event->key)) { |
188 | if (md->flags & MENU_NOMOUSE) { |
189 | if (MOUSE_BUTTONS(m->b) != 0) |
190 | return (1); |
191 | return (0); |
192 | } |
193 | if (m->x < md->px || |
194 | m->x > md->px + 4 + menu->width || |
195 | m->y < md->py + 1 || |
196 | m->y > md->py + 1 + count - 1) { |
197 | if (MOUSE_RELEASE(m->b)) |
198 | return (1); |
199 | if (md->choice != -1) { |
200 | md->choice = -1; |
201 | c->flags |= CLIENT_REDRAWOVERLAY; |
202 | } |
203 | return (0); |
204 | } |
205 | if (MOUSE_RELEASE(m->b)) |
206 | goto chosen; |
207 | md->choice = m->y - (md->py + 1); |
208 | if (md->choice != old) |
209 | c->flags |= CLIENT_REDRAWOVERLAY; |
210 | return (0); |
211 | } |
212 | for (i = 0; i < (u_int)count; i++) { |
213 | name = menu->items[i].name; |
214 | if (name == NULL || *name == '-') |
215 | continue; |
216 | if (event->key == menu->items[i].key) { |
217 | md->choice = i; |
218 | goto chosen; |
219 | } |
220 | } |
221 | switch (event->key) { |
222 | case KEYC_UP: |
223 | case 'k': |
224 | if (old == -1) |
225 | old = 0; |
226 | do { |
227 | if (md->choice == -1 || md->choice == 0) |
228 | md->choice = count - 1; |
229 | else |
230 | md->choice--; |
231 | name = menu->items[md->choice].name; |
232 | } while ((name == NULL || *name == '-') && md->choice != old); |
233 | c->flags |= CLIENT_REDRAWOVERLAY; |
234 | return (0); |
235 | case KEYC_DOWN: |
236 | case 'j': |
237 | if (old == -1) |
238 | old = 0; |
239 | do { |
240 | if (md->choice == -1 || md->choice == count - 1) |
241 | md->choice = 0; |
242 | else |
243 | md->choice++; |
244 | name = menu->items[md->choice].name; |
245 | } while ((name == NULL || *name == '-') && md->choice != old); |
246 | c->flags |= CLIENT_REDRAWOVERLAY; |
247 | return (0); |
248 | case '\r': |
249 | goto chosen; |
250 | case '\033': /* Escape */ |
251 | case '\003': /* C-c */ |
252 | case '\007': /* C-g */ |
253 | case 'q': |
254 | return (1); |
255 | } |
256 | return (0); |
257 | |
258 | chosen: |
259 | if (md->choice == -1) |
260 | return (1); |
261 | item = &menu->items[md->choice]; |
262 | if (item->name == NULL || *item->name == '-') |
263 | return (1); |
264 | if (md->cb != NULL) { |
265 | md->cb(md->menu, md->choice, item->key, md->data); |
266 | md->cb = NULL; |
267 | return (1); |
268 | } |
269 | |
270 | pr = cmd_parse_from_string(item->command, NULL); |
271 | switch (pr->status) { |
272 | case CMD_PARSE_EMPTY: |
273 | new_item = NULL; |
274 | break; |
275 | case CMD_PARSE_ERROR: |
276 | new_item = cmdq_get_error(pr->error); |
277 | free(pr->error); |
278 | cmdq_append(c, new_item); |
279 | break; |
280 | case CMD_PARSE_SUCCESS: |
281 | if (md->item != NULL) |
282 | m = &md->item->shared->mouse; |
283 | else |
284 | m = NULL; |
285 | new_item = cmdq_get_command(pr->cmdlist, &md->fs, m, 0); |
286 | cmd_list_free(pr->cmdlist); |
287 | cmdq_append(c, new_item); |
288 | break; |
289 | } |
290 | return (1); |
291 | } |
292 | |
293 | int |
294 | (struct menu *, int flags, struct cmdq_item *item, u_int px, |
295 | u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, |
296 | void *data) |
297 | { |
298 | struct menu_data *md; |
299 | |
300 | if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) |
301 | return (-1); |
302 | |
303 | md = xcalloc(1, sizeof *md); |
304 | md->item = item; |
305 | md->flags = flags; |
306 | |
307 | if (fs != NULL) |
308 | cmd_find_copy_state(&md->fs, fs); |
309 | screen_init(&md->s, menu->width + 4, menu->count + 2, 0); |
310 | |
311 | md->px = px; |
312 | md->py = py; |
313 | |
314 | md->menu = menu; |
315 | md->choice = -1; |
316 | |
317 | md->cb = cb; |
318 | md->data = data; |
319 | |
320 | server_client_set_overlay(c, 0, menu_draw_cb, menu_key_cb, menu_free_cb, |
321 | md); |
322 | return (0); |
323 | } |
324 | |