GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/mode-tree.c Lines: 0 411 0.0 %
Date: 2017-11-07 Branches: 0 275 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: mode-tree.c,v 1.11 2017/09/08 16:28:41 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2017 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 <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
26
#include "tmux.h"
27
28
struct mode_tree_item;
29
TAILQ_HEAD(mode_tree_list, mode_tree_item);
30
31
struct mode_tree_data {
32
	int			  dead;
33
	u_int			  references;
34
35
	struct window_pane	 *wp;
36
	void			 *modedata;
37
38
	const char		**sort_list;
39
	u_int			  sort_size;
40
	u_int			  sort_type;
41
42
	void			 (*buildcb)(void *, u_int, uint64_t *,
43
				     const char *);
44
	struct screen		*(*drawcb)(void *, void *, u_int, u_int);
45
	int			 (*searchcb)(void*, void *, const char *);
46
47
	struct mode_tree_list	  children;
48
	struct mode_tree_list	  saved;
49
50
	struct mode_tree_line	 *line_list;
51
	u_int			  line_size;
52
53
	u_int			  depth;
54
55
	u_int			  width;
56
	u_int			  height;
57
58
	u_int			  offset;
59
	u_int			  current;
60
61
	struct screen		  screen;
62
63
	int			  preview;
64
	char			 *search;
65
	char			 *filter;
66
};
67
68
struct mode_tree_item {
69
	struct mode_tree_item		*parent;
70
	void				*itemdata;
71
	u_int				 line;
72
73
	uint64_t			 tag;
74
	const char			*name;
75
	const char			*text;
76
77
	int				 expanded;
78
	int				 tagged;
79
80
	struct mode_tree_list		 children;
81
	TAILQ_ENTRY(mode_tree_item)	 entry;
82
};
83
84
struct mode_tree_line {
85
	struct mode_tree_item		*item;
86
	u_int				 depth;
87
	int				 last;
88
	int				 flat;
89
};
90
91
static void mode_tree_free_items(struct mode_tree_list *);
92
93
static struct mode_tree_item *
94
mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
95
{
96
	struct mode_tree_item	*mti, *child;
97
98
	TAILQ_FOREACH(mti, mtl, entry) {
99
		if (mti->tag == tag)
100
			return (mti);
101
		child = mode_tree_find_item(&mti->children, tag);
102
		if (child != NULL)
103
			return (child);
104
	}
105
	return (NULL);
106
}
107
108
static void
109
mode_tree_free_item(struct mode_tree_item *mti)
110
{
111
	mode_tree_free_items(&mti->children);
112
113
	free((void *)mti->name);
114
	free((void *)mti->text);
115
116
	free(mti);
117
}
118
119
static void
120
mode_tree_free_items(struct mode_tree_list *mtl)
121
{
122
	struct mode_tree_item	*mti, *mti1;
123
124
	TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) {
125
		TAILQ_REMOVE(mtl, mti, entry);
126
		mode_tree_free_item(mti);
127
	}
128
}
129
130
static void
131
mode_tree_check_selected(struct mode_tree_data *mtd)
132
{
133
	/*
134
	 * If the current line would now be off screen reset the offset to the
135
	 * last visible line.
136
	 */
137
	if (mtd->current > mtd->height - 1)
138
		mtd->offset = mtd->current - mtd->height + 1;
139
}
140
141
static void
142
mode_tree_clear_lines(struct mode_tree_data *mtd)
143
{
144
	free(mtd->line_list);
145
	mtd->line_list = NULL;
146
	mtd->line_size = 0;
147
}
148
149
static void
150
mode_tree_build_lines(struct mode_tree_data *mtd,
151
    struct mode_tree_list *mtl, u_int depth)
152
{
153
	struct mode_tree_item	*mti;
154
	struct mode_tree_line	*line;
155
	u_int			 i;
156
	int			 flat = 1;
157
158
	mtd->depth = depth;
159
	TAILQ_FOREACH(mti, mtl, entry) {
160
		mtd->line_list = xreallocarray(mtd->line_list,
161
		    mtd->line_size + 1, sizeof *mtd->line_list);
162
163
		line = &mtd->line_list[mtd->line_size++];
164
		line->item = mti;
165
		line->depth = depth;
166
		line->last = (mti == TAILQ_LAST(mtl, mode_tree_list));
167
168
		mti->line = (mtd->line_size - 1);
169
		if (!TAILQ_EMPTY(&mti->children))
170
			flat = 0;
171
		if (mti->expanded)
172
			mode_tree_build_lines(mtd, &mti->children, depth + 1);
173
	}
174
	TAILQ_FOREACH(mti, mtl, entry) {
175
		for (i = 0; i < mtd->line_size; i++) {
176
			line = &mtd->line_list[i];
177
			if (line->item == mti)
178
				line->flat = flat;
179
		}
180
	}
181
}
182
183
static void
184
mode_tree_clear_tagged(struct mode_tree_list *mtl)
185
{
186
	struct mode_tree_item	*mti;
187
188
	TAILQ_FOREACH(mti, mtl, entry) {
189
		mti->tagged = 0;
190
		mode_tree_clear_tagged(&mti->children);
191
	}
192
}
193
194
static void
195
mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
196
{
197
	u_int	i;
198
199
	for (i = 0; i < mtd->line_size; i++) {
200
		if (mtd->line_list[i].item->tag == tag)
201
			break;
202
	}
203
	if (i != mtd->line_size) {
204
		mtd->current = i;
205
		if (mtd->current > mtd->height - 1)
206
			mtd->offset = mtd->current - mtd->height + 1;
207
		else
208
			mtd->offset = 0;
209
	} else {
210
		mtd->current = 0;
211
		mtd->offset = 0;
212
	}
213
}
214
215
void
216
mode_tree_up(struct mode_tree_data *mtd, int wrap)
217
{
218
	if (mtd->current == 0) {
219
		if (wrap) {
220
			mtd->current = mtd->line_size - 1;
221
			if (mtd->line_size >= mtd->height)
222
				mtd->offset = mtd->line_size - mtd->height;
223
		}
224
	} else {
225
		mtd->current--;
226
		if (mtd->current < mtd->offset)
227
			mtd->offset--;
228
	}
229
}
230
231
void
232
mode_tree_down(struct mode_tree_data *mtd, int wrap)
233
{
234
	if (mtd->current == mtd->line_size - 1) {
235
		if (wrap) {
236
			mtd->current = 0;
237
			mtd->offset = 0;
238
		}
239
	} else {
240
		mtd->current++;
241
		if (mtd->current > mtd->offset + mtd->height - 1)
242
			mtd->offset++;
243
	}
244
}
245
246
void *
247
mode_tree_get_current(struct mode_tree_data *mtd)
248
{
249
	return (mtd->line_list[mtd->current].item->itemdata);
250
}
251
252
u_int
253
mode_tree_count_tagged(struct mode_tree_data *mtd)
254
{
255
	struct mode_tree_item	*mti;
256
	u_int			 i, tagged;
257
258
	tagged = 0;
259
	for (i = 0; i < mtd->line_size; i++) {
260
		mti = mtd->line_list[i].item;
261
		if (mti->tagged)
262
			tagged++;
263
	}
264
	return (tagged);
265
}
266
267
void
268
mode_tree_each_tagged(struct mode_tree_data *mtd, void (*cb)(void *, void *,
269
    key_code), key_code key, int current)
270
{
271
	struct mode_tree_item	*mti;
272
	u_int			 i;
273
	int			 fired;
274
275
	fired = 0;
276
	for (i = 0; i < mtd->line_size; i++) {
277
		mti = mtd->line_list[i].item;
278
		if (mti->tagged) {
279
			fired = 1;
280
			cb(mtd->modedata, mti->itemdata, key);
281
		}
282
	}
283
	if (!fired && current) {
284
		mti = mtd->line_list[mtd->current].item;
285
		cb(mtd->modedata, mti->itemdata, key);
286
	}
287
}
288
289
struct mode_tree_data *
290
mode_tree_start(struct window_pane *wp, struct args *args,
291
    void (*buildcb)(void *, u_int, uint64_t *, const char *),
292
    struct screen *(*drawcb)(void *, void *, u_int, u_int),
293
    int (*searchcb)(void *, void *, const char *), void *modedata,
294
    const char **sort_list, u_int sort_size, struct screen **s)
295
{
296
	struct mode_tree_data	*mtd;
297
	const char		*sort;
298
	u_int			 i;
299
300
	mtd = xcalloc(1, sizeof *mtd);
301
	mtd->references = 1;
302
303
	mtd->wp = wp;
304
	mtd->modedata = modedata;
305
306
	mtd->sort_list = sort_list;
307
	mtd->sort_size = sort_size;
308
	mtd->sort_type = 0;
309
310
	mtd->preview = !args_has(args, 'N');
311
312
	sort = args_get(args, 'O');
313
	if (sort != NULL) {
314
		for (i = 0; i < sort_size; i++) {
315
			if (strcasecmp(sort, sort_list[i]) == 0)
316
				mtd->sort_type = i;
317
		}
318
	}
319
320
	if (args_has(args, 'f'))
321
		mtd->filter = xstrdup(args_get(args, 'f'));
322
	else
323
		mtd->filter = NULL;
324
325
	mtd->buildcb = buildcb;
326
	mtd->drawcb = drawcb;
327
	mtd->searchcb = searchcb;
328
329
	TAILQ_INIT(&mtd->children);
330
331
	*s = &mtd->screen;
332
	screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
333
	(*s)->mode &= ~MODE_CURSOR;
334
335
	return (mtd);
336
}
337
338
void
339
mode_tree_build(struct mode_tree_data *mtd)
340
{
341
	struct screen	*s = &mtd->screen;
342
	uint64_t	 tag;
343
344
	if (mtd->line_list != NULL)
345
		tag = mtd->line_list[mtd->current].item->tag;
346
	else
347
		tag = 0;
348
349
	TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
350
	TAILQ_INIT(&mtd->children);
351
352
	mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter);
353
	if (TAILQ_EMPTY(&mtd->children))
354
		mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL);
355
356
	mode_tree_free_items(&mtd->saved);
357
	TAILQ_INIT(&mtd->saved);
358
359
	mode_tree_clear_lines(mtd);
360
	mode_tree_build_lines(mtd, &mtd->children, 0);
361
362
	mode_tree_set_current(mtd, tag);
363
364
	mtd->width = screen_size_x(s);
365
	if (mtd->preview) {
366
		mtd->height = (screen_size_y(s) / 3) * 2;
367
		if (mtd->height > mtd->line_size)
368
			mtd->height = screen_size_y(s) / 2;
369
		if (mtd->height < 10)
370
			mtd->height = screen_size_y(s);
371
		if (screen_size_y(s) - mtd->height < 2)
372
			mtd->height = screen_size_y(s);
373
	} else
374
		mtd->height = screen_size_y(s);
375
	mode_tree_check_selected(mtd);
376
}
377
378
static void
379
mode_tree_remove_ref(struct mode_tree_data *mtd)
380
{
381
	if (--mtd->references == 0)
382
		free(mtd);
383
}
384
385
void
386
mode_tree_free(struct mode_tree_data *mtd)
387
{
388
	mode_tree_free_items(&mtd->children);
389
	mode_tree_clear_lines(mtd);
390
	screen_free(&mtd->screen);
391
392
	free(mtd->search);
393
	free(mtd->filter);
394
395
	mtd->dead = 1;
396
	mode_tree_remove_ref(mtd);
397
}
398
399
void
400
mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
401
{
402
	struct screen	*s = &mtd->screen;
403
404
	screen_resize(s, sx, sy, 0);
405
406
	mode_tree_build(mtd);
407
	mode_tree_draw(mtd);
408
409
	mtd->wp->flags |= PANE_REDRAW;
410
}
411
412
struct mode_tree_item *
413
mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
414
    void *itemdata, uint64_t tag, const char *name, const char *text,
415
    int expanded)
416
{
417
	struct mode_tree_item	*mti, *saved;
418
419
	log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
420
	    name, text);
421
422
	mti = xcalloc(1, sizeof *mti);
423
	mti->parent = parent;
424
	mti->itemdata = itemdata;
425
426
	mti->tag = tag;
427
	mti->name = xstrdup(name);
428
	mti->text = xstrdup(text);
429
430
	saved = mode_tree_find_item(&mtd->saved, tag);
431
	if (saved != NULL) {
432
		if (parent == NULL || (parent != NULL && parent->expanded))
433
			mti->tagged = saved->tagged;
434
		mti->expanded = saved->expanded;
435
	} else if (expanded == -1)
436
		mti->expanded = 1;
437
	else
438
		mti->expanded = expanded;
439
440
	TAILQ_INIT(&mti->children);
441
442
	if (parent != NULL)
443
		TAILQ_INSERT_TAIL(&parent->children, mti, entry);
444
	else
445
		TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
446
447
	return (mti);
448
}
449
450
void
451
mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
452
{
453
	struct mode_tree_item	*parent = mti->parent;
454
455
	if (parent != NULL)
456
		TAILQ_REMOVE(&parent->children, mti, entry);
457
	else
458
		TAILQ_REMOVE(&mtd->children, mti, entry);
459
	mode_tree_free_item(mti);
460
}
461
462
void
463
mode_tree_draw(struct mode_tree_data *mtd)
464
{
465
	struct window_pane	*wp = mtd->wp;
466
	struct screen		*s = &mtd->screen, *box = NULL;
467
	struct mode_tree_line	*line;
468
	struct mode_tree_item	*mti;
469
	struct options		*oo = wp->window->options;
470
	struct screen_write_ctx	 ctx;
471
	struct grid_cell	 gc0, gc;
472
	u_int			 w, h, i, j, sy, box_x, box_y;
473
	char			*text, *start, key[7];
474
	const char		*tag, *symbol;
475
	size_t			 size;
476
	int			 keylen;
477
478
	if (mtd->line_size == 0)
479
		return;
480
481
	memcpy(&gc0, &grid_default_cell, sizeof gc0);
482
	memcpy(&gc, &grid_default_cell, sizeof gc);
483
	style_apply(&gc, oo, "mode-style");
484
485
	w = mtd->width;
486
	h = mtd->height;
487
488
	screen_write_start(&ctx, NULL, s);
489
	screen_write_clearscreen(&ctx, 8);
490
491
	if (mtd->line_size > 10)
492
		keylen = 6;
493
	else
494
		keylen = 4;
495
496
	for (i = 0; i < mtd->line_size; i++) {
497
		if (i < mtd->offset)
498
			continue;
499
		if (i > mtd->offset + h - 1)
500
			break;
501
502
		line = &mtd->line_list[i];
503
		mti = line->item;
504
505
		screen_write_cursormove(&ctx, 0, i - mtd->offset);
506
507
		if (i < 10)
508
			snprintf(key, sizeof key, "(%c)  ", '0' + i);
509
		else if (i < 36)
510
			snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
511
		else
512
			*key = '\0';
513
514
		if (line->flat)
515
			symbol = "";
516
		else if (TAILQ_EMPTY(&mti->children))
517
			symbol = "  ";
518
		else if (mti->expanded)
519
			symbol = "- ";
520
		else
521
			symbol = "+ ";
522
523
		if (line->depth == 0)
524
			start = xstrdup(symbol);
525
		else {
526
			size = (4 * line->depth) + 32;
527
528
			start = xcalloc(1, size);
529
			for (j = 1; j < line->depth; j++) {
530
				if (mti->parent != NULL &&
531
				    mtd->line_list[mti->parent->line].last)
532
					strlcat(start, "    ", size);
533
				else
534
					strlcat(start, "\001x\001   ", size);
535
			}
536
			if (line->last)
537
				strlcat(start, "\001mq\001> ", size);
538
			else
539
				strlcat(start, "\001tq\001> ", size);
540
			strlcat(start, symbol, size);
541
		}
542
543
		if (mti->tagged)
544
			tag = "*";
545
		else
546
			tag = "";
547
		xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start,
548
		    mti->name, tag, mti->text);
549
		free(start);
550
551
		if (mti->tagged) {
552
			gc.attr ^= GRID_ATTR_BRIGHT;
553
			gc0.attr ^= GRID_ATTR_BRIGHT;
554
		}
555
556
		if (i != mtd->current) {
557
			screen_write_puts(&ctx, &gc0, "%.*s", w, text);
558
			screen_write_clearendofline(&ctx, 8);
559
		} else
560
			screen_write_puts(&ctx, &gc, "%-*.*s", w, w, text);
561
		free(text);
562
563
		if (mti->tagged) {
564
			gc.attr ^= GRID_ATTR_BRIGHT;
565
			gc0.attr ^= GRID_ATTR_BRIGHT;
566
		}
567
	}
568
569
	sy = screen_size_y(s);
570
	if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
571
		screen_write_stop(&ctx);
572
		return;
573
	}
574
575
	line = &mtd->line_list[mtd->current];
576
	mti = line->item;
577
578
	screen_write_cursormove(&ctx, 0, h);
579
	screen_write_box(&ctx, w, sy - h);
580
581
	xasprintf(&text, " %s (sort: %s) ", mti->name,
582
	    mtd->sort_list[mtd->sort_type]);
583
	if (w - 2 >= strlen(text)) {
584
		screen_write_cursormove(&ctx, 1, h);
585
		screen_write_puts(&ctx, &gc0, "%s", text);
586
	}
587
	free(text);
588
589
	box_x = w - 4;
590
	box_y = sy - h - 2;
591
592
	if (box_x != 0 && box_y != 0)
593
		box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y);
594
	if (box != NULL) {
595
		screen_write_cursormove(&ctx, 2, h + 1);
596
		screen_write_copy(&ctx, box, 0, 0, box_x, box_y, NULL, NULL);
597
598
		screen_free(box);
599
	}
600
601
	screen_write_stop(&ctx);
602
}
603
604
static struct mode_tree_item *
605
mode_tree_search_for(struct mode_tree_data *mtd)
606
{
607
	struct mode_tree_item	*mti, *last, *next;
608
609
	if (mtd->search == NULL)
610
		return (NULL);
611
612
	mti = last = mtd->line_list[mtd->current].item;
613
	for (;;) {
614
		if (!TAILQ_EMPTY(&mti->children))
615
			mti = TAILQ_FIRST(&mti->children);
616
		else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
617
			mti = next;
618
		else {
619
			for (;;) {
620
				mti = mti->parent;
621
				if (mti == NULL)
622
					break;
623
				if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
624
					mti = next;
625
					break;
626
				}
627
			}
628
		}
629
		if (mti == NULL)
630
			mti = TAILQ_FIRST(&mtd->children);
631
		if (mti == last)
632
			break;
633
634
		if (mtd->searchcb == NULL) {
635
			if (strstr(mti->name, mtd->search) != NULL)
636
				return (mti);
637
			continue;
638
		}
639
		if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
640
			return (mti);
641
	}
642
	return (NULL);
643
}
644
645
static void
646
mode_tree_search_set(struct mode_tree_data *mtd)
647
{
648
	struct mode_tree_item	*mti, *loop;
649
	uint64_t		 tag;
650
651
	mti = mode_tree_search_for(mtd);
652
	if (mti == NULL)
653
		return;
654
	tag = mti->tag;
655
656
	loop = mti->parent;
657
	while (loop != NULL) {
658
		loop->expanded = 1;
659
		loop = loop->parent;
660
	}
661
662
	mode_tree_build(mtd);
663
	mode_tree_set_current(mtd, tag);
664
	mode_tree_draw(mtd);
665
	mtd->wp->flags |= PANE_REDRAW;
666
}
667
668
static int
669
mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
670
    __unused int done)
671
{
672
	struct mode_tree_data	*mtd = data;
673
674
	if (mtd->dead)
675
		return (0);
676
677
	free(mtd->search);
678
	if (s == NULL || *s == '\0') {
679
		mtd->search = NULL;
680
		return (0);
681
	}
682
	mtd->search = xstrdup(s);
683
	mode_tree_search_set(mtd);
684
685
	return (0);
686
}
687
688
static void
689
mode_tree_search_free(void *data)
690
{
691
	mode_tree_remove_ref(data);
692
}
693
694
static int
695
mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
696
    __unused int done)
697
{
698
	struct mode_tree_data	*mtd = data;
699
700
	if (mtd->dead)
701
		return (0);
702
703
	if (mtd->filter != NULL)
704
		free(mtd->filter);
705
	if (s == NULL || *s == '\0')
706
		mtd->filter = NULL;
707
	else
708
		mtd->filter = xstrdup(s);
709
710
	mode_tree_build(mtd);
711
	mode_tree_draw(mtd);
712
	mtd->wp->flags |= PANE_REDRAW;
713
714
	return (0);
715
}
716
717
static void
718
mode_tree_filter_free(void *data)
719
{
720
	mode_tree_remove_ref(data);
721
}
722
723
int
724
mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
725
    struct mouse_event *m)
726
{
727
	struct mode_tree_line	*line;
728
	struct mode_tree_item	*current, *parent;
729
	u_int			 i, x, y;
730
	int			 choice;
731
	key_code		 tmp;
732
733
	if (*key == KEYC_MOUSEDOWN1_PANE) {
734
		if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
735
			*key = KEYC_NONE;
736
			return (0);
737
		}
738
		if (x > mtd->width || y > mtd->height) {
739
			*key = KEYC_NONE;
740
			return (0);
741
		}
742
		if (mtd->offset + y < mtd->line_size) {
743
			mtd->current = mtd->offset + y;
744
			*key = '\r';
745
			return (0);
746
		}
747
	}
748
749
	line = &mtd->line_list[mtd->current];
750
	current = line->item;
751
752
	choice = -1;
753
	if (*key >= '0' && *key <= '9')
754
		choice = (*key) - '0';
755
	else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
756
		tmp = (*key) & KEYC_MASK_KEY;
757
		if (tmp >= 'a' && tmp <= 'z')
758
			choice = 10 + (tmp - 'a');
759
	}
760
	if (choice != -1) {
761
		if ((u_int)choice > mtd->line_size - 1) {
762
			*key = KEYC_NONE;
763
			return (0);
764
		}
765
		mtd->current = choice;
766
		*key = '\r';
767
		return (0);
768
	}
769
770
	switch (*key) {
771
	case 'q':
772
	case '\033': /* Escape */
773
		return (1);
774
	case KEYC_UP:
775
	case 'k':
776
	case KEYC_WHEELUP_PANE:
777
		mode_tree_up(mtd, 1);
778
		break;
779
	case KEYC_DOWN:
780
	case 'j':
781
	case KEYC_WHEELDOWN_PANE:
782
		mode_tree_down(mtd, 1);
783
		break;
784
	case KEYC_PPAGE:
785
	case '\002': /* C-b */
786
		for (i = 0; i < mtd->height; i++) {
787
			if (mtd->current == 0)
788
				break;
789
			mode_tree_up(mtd, 1);
790
		}
791
		break;
792
	case KEYC_NPAGE:
793
	case '\006': /* C-f */
794
		for (i = 0; i < mtd->height; i++) {
795
			if (mtd->current == mtd->line_size - 1)
796
				break;
797
			mode_tree_down(mtd, 1);
798
		}
799
		break;
800
	case KEYC_HOME:
801
		mtd->current = 0;
802
		mtd->offset = 0;
803
		break;
804
	case KEYC_END:
805
		mtd->current = mtd->line_size - 1;
806
		if (mtd->current > mtd->height - 1)
807
			mtd->offset = mtd->current - mtd->height + 1;
808
		else
809
			mtd->offset = 0;
810
		break;
811
	case 't':
812
		/*
813
		 * Do not allow parents and children to both be tagged: untag
814
		 * all parents and children of current.
815
		 */
816
		if (!current->tagged) {
817
			parent = current->parent;
818
			while (parent != NULL) {
819
				parent->tagged = 0;
820
				parent = parent->parent;
821
			}
822
			mode_tree_clear_tagged(&current->children);
823
			current->tagged = 1;
824
		} else
825
			current->tagged = 0;
826
		mode_tree_down(mtd, 0);
827
		break;
828
	case 'T':
829
		for (i = 0; i < mtd->line_size; i++)
830
			mtd->line_list[i].item->tagged = 0;
831
		break;
832
	case '\024': /* C-t */
833
		for (i = 0; i < mtd->line_size; i++) {
834
			if (mtd->line_list[i].item->parent == NULL)
835
				mtd->line_list[i].item->tagged = 1;
836
			else
837
				mtd->line_list[i].item->tagged = 0;
838
		}
839
		break;
840
	case 'O':
841
		mtd->sort_type++;
842
		if (mtd->sort_type == mtd->sort_size)
843
			mtd->sort_type = 0;
844
		mode_tree_build(mtd);
845
		break;
846
	case KEYC_LEFT:
847
	case '-':
848
		if (line->flat || !current->expanded)
849
			current = current->parent;
850
		if (current == NULL)
851
			mode_tree_up(mtd, 0);
852
		else {
853
			current->expanded = 0;
854
			mtd->current = current->line;
855
			mode_tree_build(mtd);
856
		}
857
		break;
858
	case KEYC_RIGHT:
859
	case '+':
860
		if (line->flat || current->expanded)
861
			mode_tree_down(mtd, 0);
862
		else if (!line->flat) {
863
			current->expanded = 1;
864
			mode_tree_build(mtd);
865
		}
866
		break;
867
	case '\023': /* C-s */
868
		mtd->references++;
869
		status_prompt_set(c, "(search) ", "",
870
		    mode_tree_search_callback, mode_tree_search_free, mtd,
871
		    PROMPT_NOFORMAT);
872
		break;
873
	case 'n':
874
		mode_tree_search_set(mtd);
875
		break;
876
	case 'f':
877
		mtd->references++;
878
		status_prompt_set(c, "(filter) ", mtd->filter,
879
		    mode_tree_filter_callback, mode_tree_filter_free, mtd,
880
		    PROMPT_NOFORMAT);
881
		break;
882
	case 'v':
883
		mtd->preview = !mtd->preview;
884
		mode_tree_build(mtd);
885
		if (mtd->preview)
886
			mode_tree_check_selected(mtd);
887
		break;
888
	}
889
	return (0);
890
}
891
892
void
893
mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
894
    const char *template, const char *name)
895
{
896
	struct cmdq_item	*new_item;
897
	struct cmd_list		*cmdlist;
898
	char			*command, *cause;
899
900
	command = cmd_template_replace(template, name, 1);
901
	if (command == NULL || *command == '\0') {
902
		free(command);
903
		return;
904
	}
905
906
	cmdlist = cmd_string_parse(command, NULL, 0, &cause);
907
	if (cmdlist == NULL) {
908
		if (cause != NULL && c != NULL) {
909
			*cause = toupper((u_char)*cause);
910
			status_message_set(c, "%s", cause);
911
		}
912
		free(cause);
913
	} else {
914
		new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
915
		cmdq_append(c, new_item);
916
		cmd_list_free(cmdlist);
917
	}
918
919
	free(command);
920
}