Annotation of src/usr.bin/tmux/mode-tree.c, Revision 1.15
1.15 ! nicm 1: /* $OpenBSD: mode-tree.c,v 1.14 2017/10/25 11:26:11 nicm Exp $ */
1.1 nicm 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 {
1.3 nicm 32: int dead;
33: u_int references;
34:
1.1 nicm 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:
1.6 nicm 42: void (*buildcb)(void *, u_int, uint64_t *,
43: const char *);
1.1 nicm 44: struct screen *(*drawcb)(void *, void *, u_int, u_int);
1.3 nicm 45: int (*searchcb)(void*, void *, const char *);
1.1 nicm 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;
1.3 nicm 62:
1.9 nicm 63: int preview;
1.6 nicm 64: char *search;
65: char *filter;
1.1 nicm 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
1.11 nicm 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
1.1 nicm 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:
1.3 nicm 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)
1.11 nicm 206: mtd->offset = mtd->current - mtd->height + 1;
1.3 nicm 207: else
208: mtd->offset = 0;
209: } else {
210: mtd->current = 0;
211: mtd->offset = 0;
212: }
213: }
214:
1.1 nicm 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 *,
1.14 nicm 269: struct client *, key_code), struct client *c, key_code key, int current)
1.1 nicm 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;
1.14 nicm 280: cb(mtd->modedata, mti->itemdata, c, key);
1.1 nicm 281: }
282: }
283: if (!fired && current) {
284: mti = mtd->line_list[mtd->current].item;
1.14 nicm 285: cb(mtd->modedata, mti->itemdata, c, key);
1.1 nicm 286: }
287: }
288:
289: struct mode_tree_data *
1.5 nicm 290: mode_tree_start(struct window_pane *wp, struct args *args,
1.6 nicm 291: void (*buildcb)(void *, u_int, uint64_t *, const char *),
1.5 nicm 292: struct screen *(*drawcb)(void *, void *, u_int, u_int),
1.3 nicm 293: int (*searchcb)(void *, void *, const char *), void *modedata,
294: const char **sort_list, u_int sort_size, struct screen **s)
1.1 nicm 295: {
296: struct mode_tree_data *mtd;
1.5 nicm 297: const char *sort;
298: u_int i;
1.1 nicm 299:
300: mtd = xcalloc(1, sizeof *mtd);
1.3 nicm 301: mtd->references = 1;
302:
1.1 nicm 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;
1.5 nicm 309:
1.9 nicm 310: mtd->preview = !args_has(args, 'N');
311:
1.5 nicm 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: }
1.1 nicm 319:
1.6 nicm 320: if (args_has(args, 'f'))
321: mtd->filter = xstrdup(args_get(args, 'f'));
322: else
323: mtd->filter = NULL;
324:
1.1 nicm 325: mtd->buildcb = buildcb;
326: mtd->drawcb = drawcb;
1.3 nicm 327: mtd->searchcb = searchcb;
1.1 nicm 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:
1.6 nicm 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);
1.1 nicm 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:
1.3 nicm 362: mode_tree_set_current(mtd, tag);
1.1 nicm 363:
364: mtd->width = screen_size_x(s);
1.9 nicm 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
1.1 nicm 374: mtd->height = screen_size_y(s);
1.11 nicm 375: mode_tree_check_selected(mtd);
1.1 nicm 376: }
377:
1.3 nicm 378: static void
379: mode_tree_remove_ref(struct mode_tree_data *mtd)
380: {
381: if (--mtd->references == 0)
382: free(mtd);
383: }
384:
1.1 nicm 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);
1.3 nicm 391:
1.6 nicm 392: free(mtd->search);
393: free(mtd->filter);
394:
1.3 nicm 395: mtd->dead = 1;
396: mode_tree_remove_ref(mtd);
1.1 nicm 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;
1.7 nicm 466: struct screen *s = &mtd->screen, *box = NULL;
1.1 nicm 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)
1.8 nicm 508: snprintf(key, sizeof key, "(%c) ", '0' + i);
1.1 nicm 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) {
1.13 nicm 557: screen_write_nputs(&ctx, w, &gc0, "%s", text);
1.1 nicm 558: screen_write_clearendofline(&ctx, 8);
1.13 nicm 559: } else {
560: screen_write_nputs(&ctx, w, &gc, "%s", text);
561: screen_write_clearendofline(&ctx, gc.bg);
562: }
1.1 nicm 563: free(text);
564:
565: if (mti->tagged) {
566: gc.attr ^= GRID_ATTR_BRIGHT;
567: gc0.attr ^= GRID_ATTR_BRIGHT;
568: }
569: }
570:
571: sy = screen_size_y(s);
1.9 nicm 572: if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
1.1 nicm 573: screen_write_stop(&ctx);
574: return;
575: }
576:
577: line = &mtd->line_list[mtd->current];
578: mti = line->item;
579:
580: screen_write_cursormove(&ctx, 0, h);
581: screen_write_box(&ctx, w, sy - h);
582:
583: xasprintf(&text, " %s (sort: %s) ", mti->name,
584: mtd->sort_list[mtd->sort_type]);
585: if (w - 2 >= strlen(text)) {
586: screen_write_cursormove(&ctx, 1, h);
587: screen_write_puts(&ctx, &gc0, "%s", text);
588: }
589: free(text);
590:
591: box_x = w - 4;
592: box_y = sy - h - 2;
593:
1.7 nicm 594: if (box_x != 0 && box_y != 0)
595: box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y);
1.1 nicm 596: if (box != NULL) {
597: screen_write_cursormove(&ctx, 2, h + 1);
598: screen_write_copy(&ctx, box, 0, 0, box_x, box_y, NULL, NULL);
599:
600: screen_free(box);
601: }
602:
603: screen_write_stop(&ctx);
604: }
605:
1.3 nicm 606: static struct mode_tree_item *
607: mode_tree_search_for(struct mode_tree_data *mtd)
608: {
609: struct mode_tree_item *mti, *last, *next;
610:
1.6 nicm 611: if (mtd->search == NULL)
1.3 nicm 612: return (NULL);
613:
614: mti = last = mtd->line_list[mtd->current].item;
615: for (;;) {
616: if (!TAILQ_EMPTY(&mti->children))
617: mti = TAILQ_FIRST(&mti->children);
618: else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
619: mti = next;
620: else {
621: for (;;) {
622: mti = mti->parent;
623: if (mti == NULL)
624: break;
625: if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
626: mti = next;
627: break;
628: }
629: }
630: }
631: if (mti == NULL)
632: mti = TAILQ_FIRST(&mtd->children);
633: if (mti == last)
634: break;
635:
636: if (mtd->searchcb == NULL) {
1.6 nicm 637: if (strstr(mti->name, mtd->search) != NULL)
1.3 nicm 638: return (mti);
639: continue;
640: }
1.6 nicm 641: if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
1.4 nicm 642: return (mti);
1.3 nicm 643: }
644: return (NULL);
645: }
646:
647: static void
648: mode_tree_search_set(struct mode_tree_data *mtd)
649: {
650: struct mode_tree_item *mti, *loop;
651: uint64_t tag;
652:
653: mti = mode_tree_search_for(mtd);
654: if (mti == NULL)
655: return;
656: tag = mti->tag;
657:
658: loop = mti->parent;
659: while (loop != NULL) {
660: loop->expanded = 1;
661: loop = loop->parent;
662: }
1.6 nicm 663:
1.3 nicm 664: mode_tree_build(mtd);
665: mode_tree_set_current(mtd, tag);
666: mode_tree_draw(mtd);
1.6 nicm 667: mtd->wp->flags |= PANE_REDRAW;
1.3 nicm 668: }
669:
670: static int
671: mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
672: __unused int done)
673: {
674: struct mode_tree_data *mtd = data;
675:
676: if (mtd->dead)
677: return (0);
678:
1.6 nicm 679: free(mtd->search);
680: if (s == NULL || *s == '\0') {
681: mtd->search = NULL;
1.3 nicm 682: return (0);
683: }
1.6 nicm 684: mtd->search = xstrdup(s);
1.3 nicm 685: mode_tree_search_set(mtd);
686:
687: return (0);
688: }
689:
690: static void
691: mode_tree_search_free(void *data)
692: {
693: mode_tree_remove_ref(data);
694: }
695:
1.6 nicm 696: static int
697: mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
698: __unused int done)
699: {
700: struct mode_tree_data *mtd = data;
701:
702: if (mtd->dead)
703: return (0);
704:
705: if (mtd->filter != NULL)
706: free(mtd->filter);
707: if (s == NULL || *s == '\0')
708: mtd->filter = NULL;
709: else
710: mtd->filter = xstrdup(s);
711:
712: mode_tree_build(mtd);
713: mode_tree_draw(mtd);
714: mtd->wp->flags |= PANE_REDRAW;
715:
716: return (0);
717: }
718:
719: static void
720: mode_tree_filter_free(void *data)
721: {
722: mode_tree_remove_ref(data);
723: }
724:
1.1 nicm 725: int
1.3 nicm 726: mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
727: struct mouse_event *m)
1.1 nicm 728: {
729: struct mode_tree_line *line;
730: struct mode_tree_item *current, *parent;
731: u_int i, x, y;
732: int choice;
733: key_code tmp;
734:
735: if (*key == KEYC_MOUSEDOWN1_PANE) {
736: if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
737: *key = KEYC_NONE;
738: return (0);
739: }
740: if (x > mtd->width || y > mtd->height) {
741: *key = KEYC_NONE;
742: return (0);
743: }
744: if (mtd->offset + y < mtd->line_size) {
745: mtd->current = mtd->offset + y;
746: *key = '\r';
747: return (0);
748: }
749: }
750:
751: line = &mtd->line_list[mtd->current];
752: current = line->item;
753:
754: choice = -1;
755: if (*key >= '0' && *key <= '9')
756: choice = (*key) - '0';
757: else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
758: tmp = (*key) & KEYC_MASK_KEY;
759: if (tmp >= 'a' && tmp <= 'z')
760: choice = 10 + (tmp - 'a');
761: }
762: if (choice != -1) {
763: if ((u_int)choice > mtd->line_size - 1) {
764: *key = KEYC_NONE;
765: return (0);
766: }
767: mtd->current = choice;
768: *key = '\r';
769: return (0);
770: }
771:
772: switch (*key) {
773: case 'q':
774: case '\033': /* Escape */
775: return (1);
776: case KEYC_UP:
777: case 'k':
778: case KEYC_WHEELUP_PANE:
1.12 nicm 779: case '\020': /* C-p */
1.1 nicm 780: mode_tree_up(mtd, 1);
781: break;
782: case KEYC_DOWN:
783: case 'j':
784: case KEYC_WHEELDOWN_PANE:
1.12 nicm 785: case '\016': /* C-n */
1.1 nicm 786: mode_tree_down(mtd, 1);
787: break;
788: case KEYC_PPAGE:
789: case '\002': /* C-b */
790: for (i = 0; i < mtd->height; i++) {
791: if (mtd->current == 0)
792: break;
793: mode_tree_up(mtd, 1);
794: }
795: break;
796: case KEYC_NPAGE:
797: case '\006': /* C-f */
798: for (i = 0; i < mtd->height; i++) {
799: if (mtd->current == mtd->line_size - 1)
800: break;
801: mode_tree_down(mtd, 1);
802: }
803: break;
804: case KEYC_HOME:
805: mtd->current = 0;
806: mtd->offset = 0;
807: break;
808: case KEYC_END:
809: mtd->current = mtd->line_size - 1;
810: if (mtd->current > mtd->height - 1)
1.11 nicm 811: mtd->offset = mtd->current - mtd->height + 1;
1.1 nicm 812: else
813: mtd->offset = 0;
814: break;
815: case 't':
816: /*
817: * Do not allow parents and children to both be tagged: untag
818: * all parents and children of current.
819: */
820: if (!current->tagged) {
821: parent = current->parent;
822: while (parent != NULL) {
823: parent->tagged = 0;
824: parent = parent->parent;
825: }
826: mode_tree_clear_tagged(¤t->children);
827: current->tagged = 1;
828: } else
829: current->tagged = 0;
830: mode_tree_down(mtd, 0);
831: break;
832: case 'T':
833: for (i = 0; i < mtd->line_size; i++)
834: mtd->line_list[i].item->tagged = 0;
835: break;
836: case '\024': /* C-t */
837: for (i = 0; i < mtd->line_size; i++) {
838: if (mtd->line_list[i].item->parent == NULL)
839: mtd->line_list[i].item->tagged = 1;
840: else
841: mtd->line_list[i].item->tagged = 0;
842: }
843: break;
844: case 'O':
845: mtd->sort_type++;
846: if (mtd->sort_type == mtd->sort_size)
847: mtd->sort_type = 0;
848: mode_tree_build(mtd);
849: break;
850: case KEYC_LEFT:
1.15 ! nicm 851: case 'h':
1.1 nicm 852: case '-':
853: if (line->flat || !current->expanded)
854: current = current->parent;
855: if (current == NULL)
856: mode_tree_up(mtd, 0);
857: else {
858: current->expanded = 0;
859: mtd->current = current->line;
860: mode_tree_build(mtd);
861: }
862: break;
863: case KEYC_RIGHT:
1.15 ! nicm 864: case 'l':
1.1 nicm 865: case '+':
866: if (line->flat || current->expanded)
867: mode_tree_down(mtd, 0);
868: else if (!line->flat) {
869: current->expanded = 1;
870: mode_tree_build(mtd);
871: }
1.3 nicm 872: break;
873: case '\023': /* C-s */
874: mtd->references++;
875: status_prompt_set(c, "(search) ", "",
876: mode_tree_search_callback, mode_tree_search_free, mtd,
877: PROMPT_NOFORMAT);
878: break;
879: case 'n':
880: mode_tree_search_set(mtd);
1.6 nicm 881: break;
882: case 'f':
883: mtd->references++;
884: status_prompt_set(c, "(filter) ", mtd->filter,
885: mode_tree_filter_callback, mode_tree_filter_free, mtd,
886: PROMPT_NOFORMAT);
1.9 nicm 887: break;
888: case 'v':
889: mtd->preview = !mtd->preview;
890: mode_tree_build(mtd);
1.11 nicm 891: if (mtd->preview)
892: mode_tree_check_selected(mtd);
1.1 nicm 893: break;
894: }
895: return (0);
896: }
897:
898: void
899: mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
900: const char *template, const char *name)
901: {
902: struct cmdq_item *new_item;
903: struct cmd_list *cmdlist;
904: char *command, *cause;
905:
906: command = cmd_template_replace(template, name, 1);
1.2 nicm 907: if (command == NULL || *command == '\0') {
908: free(command);
1.1 nicm 909: return;
1.2 nicm 910: }
1.1 nicm 911:
912: cmdlist = cmd_string_parse(command, NULL, 0, &cause);
913: if (cmdlist == NULL) {
914: if (cause != NULL && c != NULL) {
915: *cause = toupper((u_char)*cause);
916: status_message_set(c, "%s", cause);
917: }
918: free(cause);
919: } else {
920: new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
921: cmdq_append(c, new_item);
922: cmd_list_free(cmdlist);
923: }
924:
925: free(command);
926: }