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