Annotation of src/usr.bin/tmux/mode-tree.c, Revision 1.17
1.17 ! nicm 1: /* $OpenBSD: mode-tree.c,v 1.16 2017/11/02 21:29:17 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:
1.3 nicm 193: static void
194: mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
195: {
196: u_int i;
197:
198: for (i = 0; i < mtd->line_size; i++) {
199: if (mtd->line_list[i].item->tag == tag)
200: break;
201: }
202: if (i != mtd->line_size) {
203: mtd->current = i;
204: if (mtd->current > mtd->height - 1)
1.11 nicm 205: mtd->offset = mtd->current - mtd->height + 1;
1.3 nicm 206: else
207: mtd->offset = 0;
208: } else {
209: mtd->current = 0;
210: mtd->offset = 0;
211: }
212: }
213:
1.1 nicm 214: void
215: mode_tree_up(struct mode_tree_data *mtd, int wrap)
216: {
217: if (mtd->current == 0) {
218: if (wrap) {
219: mtd->current = mtd->line_size - 1;
220: if (mtd->line_size >= mtd->height)
221: mtd->offset = mtd->line_size - mtd->height;
222: }
223: } else {
224: mtd->current--;
225: if (mtd->current < mtd->offset)
226: mtd->offset--;
227: }
228: }
229:
230: void
231: mode_tree_down(struct mode_tree_data *mtd, int wrap)
232: {
233: if (mtd->current == mtd->line_size - 1) {
234: if (wrap) {
235: mtd->current = 0;
236: mtd->offset = 0;
237: }
238: } else {
239: mtd->current++;
240: if (mtd->current > mtd->offset + mtd->height - 1)
241: mtd->offset++;
242: }
243: }
244:
245: void *
246: mode_tree_get_current(struct mode_tree_data *mtd)
247: {
248: return (mtd->line_list[mtd->current].item->itemdata);
249: }
250:
251: u_int
252: mode_tree_count_tagged(struct mode_tree_data *mtd)
253: {
254: struct mode_tree_item *mti;
255: u_int i, tagged;
256:
257: tagged = 0;
258: for (i = 0; i < mtd->line_size; i++) {
259: mti = mtd->line_list[i].item;
260: if (mti->tagged)
261: tagged++;
262: }
263: return (tagged);
264: }
265:
266: void
1.17 ! nicm 267: mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb,
! 268: struct client *c, key_code key, int current)
1.1 nicm 269: {
270: struct mode_tree_item *mti;
271: u_int i;
272: int fired;
273:
274: fired = 0;
275: for (i = 0; i < mtd->line_size; i++) {
276: mti = mtd->line_list[i].item;
277: if (mti->tagged) {
278: fired = 1;
1.14 nicm 279: cb(mtd->modedata, mti->itemdata, c, key);
1.1 nicm 280: }
281: }
282: if (!fired && current) {
283: mti = mtd->line_list[mtd->current].item;
1.14 nicm 284: cb(mtd->modedata, mti->itemdata, c, key);
1.1 nicm 285: }
286: }
287:
288: struct mode_tree_data *
1.5 nicm 289: mode_tree_start(struct window_pane *wp, struct args *args,
1.17 ! nicm 290: mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
! 291: mode_tree_search_cb searchcb, void *modedata, const char **sort_list,
! 292: u_int sort_size, struct screen **s)
1.1 nicm 293: {
294: struct mode_tree_data *mtd;
1.5 nicm 295: const char *sort;
296: u_int i;
1.1 nicm 297:
298: mtd = xcalloc(1, sizeof *mtd);
1.3 nicm 299: mtd->references = 1;
300:
1.1 nicm 301: mtd->wp = wp;
302: mtd->modedata = modedata;
303:
304: mtd->sort_list = sort_list;
305: mtd->sort_size = sort_size;
306: mtd->sort_type = 0;
1.5 nicm 307:
1.9 nicm 308: mtd->preview = !args_has(args, 'N');
309:
1.5 nicm 310: sort = args_get(args, 'O');
311: if (sort != NULL) {
312: for (i = 0; i < sort_size; i++) {
313: if (strcasecmp(sort, sort_list[i]) == 0)
314: mtd->sort_type = i;
315: }
316: }
1.1 nicm 317:
1.6 nicm 318: if (args_has(args, 'f'))
319: mtd->filter = xstrdup(args_get(args, 'f'));
320: else
321: mtd->filter = NULL;
322:
1.1 nicm 323: mtd->buildcb = buildcb;
324: mtd->drawcb = drawcb;
1.3 nicm 325: mtd->searchcb = searchcb;
1.1 nicm 326:
327: TAILQ_INIT(&mtd->children);
328:
329: *s = &mtd->screen;
330: screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
331: (*s)->mode &= ~MODE_CURSOR;
332:
333: return (mtd);
334: }
335:
336: void
337: mode_tree_build(struct mode_tree_data *mtd)
338: {
339: struct screen *s = &mtd->screen;
340: uint64_t tag;
341:
342: if (mtd->line_list != NULL)
343: tag = mtd->line_list[mtd->current].item->tag;
344: else
345: tag = 0;
346:
347: TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
348: TAILQ_INIT(&mtd->children);
349:
1.6 nicm 350: mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter);
351: if (TAILQ_EMPTY(&mtd->children))
352: mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL);
1.1 nicm 353:
354: mode_tree_free_items(&mtd->saved);
355: TAILQ_INIT(&mtd->saved);
356:
357: mode_tree_clear_lines(mtd);
358: mode_tree_build_lines(mtd, &mtd->children, 0);
359:
1.3 nicm 360: mode_tree_set_current(mtd, tag);
1.1 nicm 361:
362: mtd->width = screen_size_x(s);
1.9 nicm 363: if (mtd->preview) {
364: mtd->height = (screen_size_y(s) / 3) * 2;
365: if (mtd->height > mtd->line_size)
366: mtd->height = screen_size_y(s) / 2;
367: if (mtd->height < 10)
368: mtd->height = screen_size_y(s);
369: if (screen_size_y(s) - mtd->height < 2)
370: mtd->height = screen_size_y(s);
371: } else
1.1 nicm 372: mtd->height = screen_size_y(s);
1.11 nicm 373: mode_tree_check_selected(mtd);
1.1 nicm 374: }
375:
1.3 nicm 376: static void
377: mode_tree_remove_ref(struct mode_tree_data *mtd)
378: {
379: if (--mtd->references == 0)
380: free(mtd);
381: }
382:
1.1 nicm 383: void
384: mode_tree_free(struct mode_tree_data *mtd)
385: {
386: mode_tree_free_items(&mtd->children);
387: mode_tree_clear_lines(mtd);
388: screen_free(&mtd->screen);
1.3 nicm 389:
1.6 nicm 390: free(mtd->search);
391: free(mtd->filter);
392:
1.3 nicm 393: mtd->dead = 1;
394: mode_tree_remove_ref(mtd);
1.1 nicm 395: }
396:
397: void
398: mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
399: {
400: struct screen *s = &mtd->screen;
401:
402: screen_resize(s, sx, sy, 0);
403:
404: mode_tree_build(mtd);
405: mode_tree_draw(mtd);
406:
407: mtd->wp->flags |= PANE_REDRAW;
408: }
409:
410: struct mode_tree_item *
411: mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
412: void *itemdata, uint64_t tag, const char *name, const char *text,
413: int expanded)
414: {
415: struct mode_tree_item *mti, *saved;
416:
417: log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
418: name, text);
419:
420: mti = xcalloc(1, sizeof *mti);
421: mti->parent = parent;
422: mti->itemdata = itemdata;
423:
424: mti->tag = tag;
425: mti->name = xstrdup(name);
426: mti->text = xstrdup(text);
427:
428: saved = mode_tree_find_item(&mtd->saved, tag);
429: if (saved != NULL) {
430: if (parent == NULL || (parent != NULL && parent->expanded))
431: mti->tagged = saved->tagged;
432: mti->expanded = saved->expanded;
433: } else if (expanded == -1)
434: mti->expanded = 1;
435: else
436: mti->expanded = expanded;
437:
438: TAILQ_INIT(&mti->children);
439:
440: if (parent != NULL)
441: TAILQ_INSERT_TAIL(&parent->children, mti, entry);
442: else
443: TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
444:
445: return (mti);
446: }
447:
448: void
449: mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
450: {
451: struct mode_tree_item *parent = mti->parent;
452:
453: if (parent != NULL)
454: TAILQ_REMOVE(&parent->children, mti, entry);
455: else
456: TAILQ_REMOVE(&mtd->children, mti, entry);
457: mode_tree_free_item(mti);
458: }
459:
460: void
461: mode_tree_draw(struct mode_tree_data *mtd)
462: {
463: struct window_pane *wp = mtd->wp;
1.17 ! nicm 464: struct screen *s = &mtd->screen;
1.1 nicm 465: struct mode_tree_line *line;
466: struct mode_tree_item *mti;
467: struct options *oo = wp->window->options;
468: struct screen_write_ctx ctx;
469: struct grid_cell gc0, gc;
470: u_int w, h, i, j, sy, box_x, box_y;
471: char *text, *start, key[7];
472: const char *tag, *symbol;
473: size_t size;
474: int keylen;
475:
476: if (mtd->line_size == 0)
477: return;
478:
479: memcpy(&gc0, &grid_default_cell, sizeof gc0);
480: memcpy(&gc, &grid_default_cell, sizeof gc);
481: style_apply(&gc, oo, "mode-style");
482:
483: w = mtd->width;
484: h = mtd->height;
485:
486: screen_write_start(&ctx, NULL, s);
487: screen_write_clearscreen(&ctx, 8);
488:
489: if (mtd->line_size > 10)
490: keylen = 6;
491: else
492: keylen = 4;
493:
494: for (i = 0; i < mtd->line_size; i++) {
495: if (i < mtd->offset)
496: continue;
497: if (i > mtd->offset + h - 1)
498: break;
499:
500: line = &mtd->line_list[i];
501: mti = line->item;
502:
503: screen_write_cursormove(&ctx, 0, i - mtd->offset);
504:
505: if (i < 10)
1.8 nicm 506: snprintf(key, sizeof key, "(%c) ", '0' + i);
1.1 nicm 507: else if (i < 36)
508: snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
509: else
510: *key = '\0';
511:
512: if (line->flat)
513: symbol = "";
514: else if (TAILQ_EMPTY(&mti->children))
515: symbol = " ";
516: else if (mti->expanded)
517: symbol = "- ";
518: else
519: symbol = "+ ";
520:
521: if (line->depth == 0)
522: start = xstrdup(symbol);
523: else {
524: size = (4 * line->depth) + 32;
525:
526: start = xcalloc(1, size);
527: for (j = 1; j < line->depth; j++) {
528: if (mti->parent != NULL &&
529: mtd->line_list[mti->parent->line].last)
530: strlcat(start, " ", size);
531: else
532: strlcat(start, "\001x\001 ", size);
533: }
534: if (line->last)
535: strlcat(start, "\001mq\001> ", size);
536: else
537: strlcat(start, "\001tq\001> ", size);
538: strlcat(start, symbol, size);
539: }
540:
541: if (mti->tagged)
542: tag = "*";
543: else
544: tag = "";
545: xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start,
546: mti->name, tag, mti->text);
547: free(start);
548:
549: if (mti->tagged) {
550: gc.attr ^= GRID_ATTR_BRIGHT;
551: gc0.attr ^= GRID_ATTR_BRIGHT;
552: }
553:
554: if (i != mtd->current) {
1.13 nicm 555: screen_write_nputs(&ctx, w, &gc0, "%s", text);
1.1 nicm 556: screen_write_clearendofline(&ctx, 8);
1.13 nicm 557: } else {
558: screen_write_nputs(&ctx, w, &gc, "%s", text);
559: screen_write_clearendofline(&ctx, gc.bg);
560: }
1.1 nicm 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);
1.9 nicm 570: if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
1.1 nicm 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:
1.17 ! nicm 592: if (box_x != 0 && box_y != 0) {
1.1 nicm 593: screen_write_cursormove(&ctx, 2, h + 1);
1.17 ! nicm 594: mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y);
1.1 nicm 595: }
596:
597: screen_write_stop(&ctx);
598: }
599:
1.3 nicm 600: static struct mode_tree_item *
601: mode_tree_search_for(struct mode_tree_data *mtd)
602: {
603: struct mode_tree_item *mti, *last, *next;
604:
1.6 nicm 605: if (mtd->search == NULL)
1.3 nicm 606: return (NULL);
607:
608: mti = last = mtd->line_list[mtd->current].item;
609: for (;;) {
610: if (!TAILQ_EMPTY(&mti->children))
611: mti = TAILQ_FIRST(&mti->children);
612: else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
613: mti = next;
614: else {
615: for (;;) {
616: mti = mti->parent;
617: if (mti == NULL)
618: break;
619: if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
620: mti = next;
621: break;
622: }
623: }
624: }
625: if (mti == NULL)
626: mti = TAILQ_FIRST(&mtd->children);
627: if (mti == last)
628: break;
629:
630: if (mtd->searchcb == NULL) {
1.6 nicm 631: if (strstr(mti->name, mtd->search) != NULL)
1.3 nicm 632: return (mti);
633: continue;
634: }
1.6 nicm 635: if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
1.4 nicm 636: return (mti);
1.3 nicm 637: }
638: return (NULL);
639: }
640:
641: static void
642: mode_tree_search_set(struct mode_tree_data *mtd)
643: {
644: struct mode_tree_item *mti, *loop;
645: uint64_t tag;
646:
647: mti = mode_tree_search_for(mtd);
648: if (mti == NULL)
649: return;
650: tag = mti->tag;
651:
652: loop = mti->parent;
653: while (loop != NULL) {
654: loop->expanded = 1;
655: loop = loop->parent;
656: }
1.6 nicm 657:
1.3 nicm 658: mode_tree_build(mtd);
659: mode_tree_set_current(mtd, tag);
660: mode_tree_draw(mtd);
1.6 nicm 661: mtd->wp->flags |= PANE_REDRAW;
1.3 nicm 662: }
663:
664: static int
665: mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
666: __unused int done)
667: {
668: struct mode_tree_data *mtd = data;
669:
670: if (mtd->dead)
671: return (0);
672:
1.6 nicm 673: free(mtd->search);
674: if (s == NULL || *s == '\0') {
675: mtd->search = NULL;
1.3 nicm 676: return (0);
677: }
1.6 nicm 678: mtd->search = xstrdup(s);
1.3 nicm 679: mode_tree_search_set(mtd);
680:
681: return (0);
682: }
683:
684: static void
685: mode_tree_search_free(void *data)
686: {
687: mode_tree_remove_ref(data);
688: }
689:
1.6 nicm 690: static int
691: mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
692: __unused int done)
693: {
694: struct mode_tree_data *mtd = data;
695:
696: if (mtd->dead)
697: return (0);
698:
699: if (mtd->filter != NULL)
700: free(mtd->filter);
701: if (s == NULL || *s == '\0')
702: mtd->filter = NULL;
703: else
704: mtd->filter = xstrdup(s);
705:
706: mode_tree_build(mtd);
707: mode_tree_draw(mtd);
708: mtd->wp->flags |= PANE_REDRAW;
709:
710: return (0);
711: }
712:
713: static void
714: mode_tree_filter_free(void *data)
715: {
716: mode_tree_remove_ref(data);
717: }
718:
1.1 nicm 719: int
1.3 nicm 720: mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
721: struct mouse_event *m)
1.1 nicm 722: {
723: struct mode_tree_line *line;
724: struct mode_tree_item *current, *parent;
725: u_int i, x, y;
726: int choice;
727: key_code tmp;
728:
729: if (*key == KEYC_MOUSEDOWN1_PANE) {
730: if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
731: *key = KEYC_NONE;
732: return (0);
733: }
734: if (x > mtd->width || y > mtd->height) {
735: *key = KEYC_NONE;
736: return (0);
737: }
738: if (mtd->offset + y < mtd->line_size) {
739: mtd->current = mtd->offset + y;
740: *key = '\r';
741: return (0);
742: }
743: }
744:
745: line = &mtd->line_list[mtd->current];
746: current = line->item;
747:
748: choice = -1;
749: if (*key >= '0' && *key <= '9')
750: choice = (*key) - '0';
751: else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
752: tmp = (*key) & KEYC_MASK_KEY;
753: if (tmp >= 'a' && tmp <= 'z')
754: choice = 10 + (tmp - 'a');
755: }
756: if (choice != -1) {
757: if ((u_int)choice > mtd->line_size - 1) {
758: *key = KEYC_NONE;
759: return (0);
760: }
761: mtd->current = choice;
762: *key = '\r';
763: return (0);
764: }
765:
766: switch (*key) {
767: case 'q':
768: case '\033': /* Escape */
769: return (1);
770: case KEYC_UP:
771: case 'k':
772: case KEYC_WHEELUP_PANE:
1.12 nicm 773: case '\020': /* C-p */
1.1 nicm 774: mode_tree_up(mtd, 1);
775: break;
776: case KEYC_DOWN:
777: case 'j':
778: case KEYC_WHEELDOWN_PANE:
1.12 nicm 779: case '\016': /* C-n */
1.1 nicm 780: mode_tree_down(mtd, 1);
781: break;
782: case KEYC_PPAGE:
783: case '\002': /* C-b */
784: for (i = 0; i < mtd->height; i++) {
785: if (mtd->current == 0)
786: break;
787: mode_tree_up(mtd, 1);
788: }
789: break;
790: case KEYC_NPAGE:
791: case '\006': /* C-f */
792: for (i = 0; i < mtd->height; i++) {
793: if (mtd->current == mtd->line_size - 1)
794: break;
795: mode_tree_down(mtd, 1);
796: }
797: break;
798: case KEYC_HOME:
799: mtd->current = 0;
800: mtd->offset = 0;
801: break;
802: case KEYC_END:
803: mtd->current = mtd->line_size - 1;
804: if (mtd->current > mtd->height - 1)
1.11 nicm 805: mtd->offset = mtd->current - mtd->height + 1;
1.1 nicm 806: else
807: mtd->offset = 0;
808: break;
809: case 't':
810: /*
811: * Do not allow parents and children to both be tagged: untag
812: * all parents and children of current.
813: */
814: if (!current->tagged) {
815: parent = current->parent;
816: while (parent != NULL) {
817: parent->tagged = 0;
818: parent = parent->parent;
819: }
820: mode_tree_clear_tagged(¤t->children);
821: current->tagged = 1;
822: } else
823: current->tagged = 0;
824: mode_tree_down(mtd, 0);
825: break;
826: case 'T':
827: for (i = 0; i < mtd->line_size; i++)
828: mtd->line_list[i].item->tagged = 0;
829: break;
830: case '\024': /* C-t */
831: for (i = 0; i < mtd->line_size; i++) {
832: if (mtd->line_list[i].item->parent == NULL)
833: mtd->line_list[i].item->tagged = 1;
834: else
835: mtd->line_list[i].item->tagged = 0;
836: }
837: break;
838: case 'O':
839: mtd->sort_type++;
840: if (mtd->sort_type == mtd->sort_size)
841: mtd->sort_type = 0;
842: mode_tree_build(mtd);
843: break;
844: case KEYC_LEFT:
1.15 nicm 845: case 'h':
1.1 nicm 846: case '-':
847: if (line->flat || !current->expanded)
848: current = current->parent;
849: if (current == NULL)
850: mode_tree_up(mtd, 0);
851: else {
852: current->expanded = 0;
853: mtd->current = current->line;
854: mode_tree_build(mtd);
855: }
856: break;
857: case KEYC_RIGHT:
1.15 nicm 858: case 'l':
1.1 nicm 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: }
1.3 nicm 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);
1.6 nicm 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);
1.9 nicm 881: break;
882: case 'v':
883: mtd->preview = !mtd->preview;
884: mode_tree_build(mtd);
1.11 nicm 885: if (mtd->preview)
886: mode_tree_check_selected(mtd);
1.1 nicm 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);
1.2 nicm 901: if (command == NULL || *command == '\0') {
902: free(command);
1.1 nicm 903: return;
1.2 nicm 904: }
1.1 nicm 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: }