GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/window-choose.c Lines: 0 433 0.0 %
Date: 2016-12-06 Branches: 0 204 0.0 %

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