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

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