Annotation of src/usr.bin/tmux/mode-tree.c, Revision 1.20
1.20 ! nicm 1: /* $OpenBSD: mode-tree.c,v 1.19 2017/11/03 17:02:33 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.17 nicm 42: mode_tree_build_cb buildcb;
43: mode_tree_draw_cb drawcb;
44: mode_tree_search_cb searchcb;
1.1 nicm 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;
1.3 nicm 61:
1.9 nicm 62: int preview;
1.6 nicm 63: char *search;
64: char *filter;
1.1 nicm 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
1.11 nicm 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
1.1 nicm 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:
1.19 nicm 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:
1.1 nicm 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
1.17 nicm 276: mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb,
277: struct client *c, key_code key, int current)
1.1 nicm 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;
1.14 nicm 288: cb(mtd->modedata, mti->itemdata, c, key);
1.1 nicm 289: }
290: }
291: if (!fired && current) {
292: mti = mtd->line_list[mtd->current].item;
1.14 nicm 293: cb(mtd->modedata, mti->itemdata, c, key);
1.1 nicm 294: }
295: }
296:
297: struct mode_tree_data *
1.5 nicm 298: mode_tree_start(struct window_pane *wp, struct args *args,
1.17 nicm 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)
1.1 nicm 302: {
303: struct mode_tree_data *mtd;
1.5 nicm 304: const char *sort;
305: u_int i;
1.1 nicm 306:
307: mtd = xcalloc(1, sizeof *mtd);
1.3 nicm 308: mtd->references = 1;
309:
1.1 nicm 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;
1.5 nicm 316:
1.9 nicm 317: mtd->preview = !args_has(args, 'N');
318:
1.5 nicm 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: }
1.1 nicm 326:
1.6 nicm 327: if (args_has(args, 'f'))
328: mtd->filter = xstrdup(args_get(args, 'f'));
329: else
330: mtd->filter = NULL;
331:
1.1 nicm 332: mtd->buildcb = buildcb;
333: mtd->drawcb = drawcb;
1.3 nicm 334: mtd->searchcb = searchcb;
1.1 nicm 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:
1.6 nicm 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);
1.1 nicm 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:
1.3 nicm 369: mode_tree_set_current(mtd, tag);
1.1 nicm 370:
371: mtd->width = screen_size_x(s);
1.9 nicm 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
1.1 nicm 381: mtd->height = screen_size_y(s);
1.11 nicm 382: mode_tree_check_selected(mtd);
1.1 nicm 383: }
384:
1.3 nicm 385: static void
386: mode_tree_remove_ref(struct mode_tree_data *mtd)
387: {
388: if (--mtd->references == 0)
389: free(mtd);
390: }
391:
1.1 nicm 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);
1.3 nicm 398:
1.6 nicm 399: free(mtd->search);
400: free(mtd->filter);
401:
1.3 nicm 402: mtd->dead = 1;
403: mode_tree_remove_ref(mtd);
1.1 nicm 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;
1.17 nicm 473: struct screen *s = &mtd->screen;
1.1 nicm 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)
1.8 nicm 515: snprintf(key, sizeof key, "(%c) ", '0' + i);
1.1 nicm 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) {
1.13 nicm 564: screen_write_nputs(&ctx, w, &gc0, "%s", text);
1.1 nicm 565: screen_write_clearendofline(&ctx, 8);
1.13 nicm 566: } else {
567: screen_write_nputs(&ctx, w, &gc, "%s", text);
568: screen_write_clearendofline(&ctx, gc.bg);
569: }
1.1 nicm 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);
1.9 nicm 579: if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
1.1 nicm 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:
1.17 nicm 601: if (box_x != 0 && box_y != 0) {
1.1 nicm 602: screen_write_cursormove(&ctx, 2, h + 1);
1.17 nicm 603: mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y);
1.1 nicm 604: }
605:
606: screen_write_stop(&ctx);
607: }
608:
1.3 nicm 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:
1.6 nicm 614: if (mtd->search == NULL)
1.3 nicm 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) {
1.6 nicm 640: if (strstr(mti->name, mtd->search) != NULL)
1.3 nicm 641: return (mti);
642: continue;
643: }
1.6 nicm 644: if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
1.4 nicm 645: return (mti);
1.3 nicm 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: }
1.6 nicm 666:
1.3 nicm 667: mode_tree_build(mtd);
668: mode_tree_set_current(mtd, tag);
669: mode_tree_draw(mtd);
1.6 nicm 670: mtd->wp->flags |= PANE_REDRAW;
1.3 nicm 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:
1.6 nicm 682: free(mtd->search);
683: if (s == NULL || *s == '\0') {
684: mtd->search = NULL;
1.3 nicm 685: return (0);
686: }
1.6 nicm 687: mtd->search = xstrdup(s);
1.3 nicm 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:
1.6 nicm 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:
1.1 nicm 728: int
1.3 nicm 729: mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
1.19 nicm 730: struct mouse_event *m, u_int *xp, u_int *yp)
1.1 nicm 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:
1.18 nicm 738: if (KEYC_IS_MOUSE(*key)) {
1.1 nicm 739: if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
740: *key = KEYC_NONE;
741: return (0);
742: }
1.19 nicm 743: if (xp != NULL)
744: *xp = x;
745: if (yp != NULL)
746: *yp = y;
1.1 nicm 747: if (x > mtd->width || y > mtd->height) {
1.20 ! nicm 748: if (!mtd->preview)
1.19 nicm 749: *key = KEYC_NONE;
1.1 nicm 750: return (0);
751: }
752: if (mtd->offset + y < mtd->line_size) {
1.18 nicm 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';
1.20 ! nicm 758: else
! 759: *key = KEYC_NONE;
! 760: } else
! 761: *key = KEYC_NONE;
! 762: return (0);
1.1 nicm 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:
1.12 nicm 793: case '\020': /* C-p */
1.1 nicm 794: mode_tree_up(mtd, 1);
795: break;
796: case KEYC_DOWN:
797: case 'j':
798: case KEYC_WHEELDOWN_PANE:
1.12 nicm 799: case '\016': /* C-n */
1.1 nicm 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)
1.11 nicm 825: mtd->offset = mtd->current - mtd->height + 1;
1.1 nicm 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(¤t->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:
1.15 nicm 865: case 'h':
1.1 nicm 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:
1.15 nicm 878: case 'l':
1.1 nicm 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: }
1.3 nicm 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);
1.6 nicm 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);
1.9 nicm 901: break;
902: case 'v':
903: mtd->preview = !mtd->preview;
904: mode_tree_build(mtd);
1.11 nicm 905: if (mtd->preview)
906: mode_tree_check_selected(mtd);
1.1 nicm 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);
1.2 nicm 921: if (command == NULL || *command == '\0') {
922: free(command);
1.1 nicm 923: return;
1.2 nicm 924: }
1.1 nicm 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: }