Annotation of src/usr.bin/tmux/mode-tree.c, Revision 1.11
1.11 ! nicm 1: /* $OpenBSD: mode-tree.c,v 1.10 2017/08/27 09:08:36 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 *,
269: key_code), key_code key, int current)
270: {
271: struct mode_tree_item *mti;
272: u_int i;
273: int fired;
274:
275: fired = 0;
276: for (i = 0; i < mtd->line_size; i++) {
277: mti = mtd->line_list[i].item;
278: if (mti->tagged) {
279: fired = 1;
280: cb(mtd->modedata, mti->itemdata, key);
281: }
282: }
283: if (!fired && current) {
284: mti = mtd->line_list[mtd->current].item;
285: cb(mtd->modedata, mti->itemdata, key);
286: }
287: }
288:
289: struct mode_tree_data *
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) {
557: screen_write_puts(&ctx, &gc0, "%.*s", w, text);
558: screen_write_clearendofline(&ctx, 8);
559: } else
560: screen_write_puts(&ctx, &gc, "%-*.*s", w, w, text);
561: free(text);
562:
563: if (mti->tagged) {
564: gc.attr ^= GRID_ATTR_BRIGHT;
565: gc0.attr ^= GRID_ATTR_BRIGHT;
566: }
567: }
568:
569: sy = screen_size_y(s);
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.7 nicm 592: if (box_x != 0 && box_y != 0)
593: box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y);
1.1 nicm 594: if (box != NULL) {
595: screen_write_cursormove(&ctx, 2, h + 1);
596: screen_write_copy(&ctx, box, 0, 0, box_x, box_y, NULL, NULL);
597:
598: screen_free(box);
599: }
600:
601: screen_write_stop(&ctx);
602: }
603:
1.3 nicm 604: static struct mode_tree_item *
605: mode_tree_search_for(struct mode_tree_data *mtd)
606: {
607: struct mode_tree_item *mti, *last, *next;
608:
1.6 nicm 609: if (mtd->search == NULL)
1.3 nicm 610: return (NULL);
611:
612: mti = last = mtd->line_list[mtd->current].item;
613: for (;;) {
614: if (!TAILQ_EMPTY(&mti->children))
615: mti = TAILQ_FIRST(&mti->children);
616: else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
617: mti = next;
618: else {
619: for (;;) {
620: mti = mti->parent;
621: if (mti == NULL)
622: break;
623: if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
624: mti = next;
625: break;
626: }
627: }
628: }
629: if (mti == NULL)
630: mti = TAILQ_FIRST(&mtd->children);
631: if (mti == last)
632: break;
633:
634: if (mtd->searchcb == NULL) {
1.6 nicm 635: if (strstr(mti->name, mtd->search) != NULL)
1.3 nicm 636: return (mti);
637: continue;
638: }
1.6 nicm 639: if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
1.4 nicm 640: return (mti);
1.3 nicm 641: }
642: return (NULL);
643: }
644:
645: static void
646: mode_tree_search_set(struct mode_tree_data *mtd)
647: {
648: struct mode_tree_item *mti, *loop;
649: uint64_t tag;
650:
651: mti = mode_tree_search_for(mtd);
652: if (mti == NULL)
653: return;
654: tag = mti->tag;
655:
656: loop = mti->parent;
657: while (loop != NULL) {
658: loop->expanded = 1;
659: loop = loop->parent;
660: }
1.6 nicm 661:
1.3 nicm 662: mode_tree_build(mtd);
663: mode_tree_set_current(mtd, tag);
664: mode_tree_draw(mtd);
1.6 nicm 665: mtd->wp->flags |= PANE_REDRAW;
1.3 nicm 666: }
667:
668: static int
669: mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
670: __unused int done)
671: {
672: struct mode_tree_data *mtd = data;
673:
674: if (mtd->dead)
675: return (0);
676:
1.6 nicm 677: free(mtd->search);
678: if (s == NULL || *s == '\0') {
679: mtd->search = NULL;
1.3 nicm 680: return (0);
681: }
1.6 nicm 682: mtd->search = xstrdup(s);
1.3 nicm 683: mode_tree_search_set(mtd);
684:
685: return (0);
686: }
687:
688: static void
689: mode_tree_search_free(void *data)
690: {
691: mode_tree_remove_ref(data);
692: }
693:
1.6 nicm 694: static int
695: mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
696: __unused int done)
697: {
698: struct mode_tree_data *mtd = data;
699:
700: if (mtd->dead)
701: return (0);
702:
703: if (mtd->filter != NULL)
704: free(mtd->filter);
705: if (s == NULL || *s == '\0')
706: mtd->filter = NULL;
707: else
708: mtd->filter = xstrdup(s);
709:
710: mode_tree_build(mtd);
711: mode_tree_draw(mtd);
712: mtd->wp->flags |= PANE_REDRAW;
713:
714: return (0);
715: }
716:
717: static void
718: mode_tree_filter_free(void *data)
719: {
720: mode_tree_remove_ref(data);
721: }
722:
1.1 nicm 723: int
1.3 nicm 724: mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
725: struct mouse_event *m)
1.1 nicm 726: {
727: struct mode_tree_line *line;
728: struct mode_tree_item *current, *parent;
729: u_int i, x, y;
730: int choice;
731: key_code tmp;
732:
733: if (*key == KEYC_MOUSEDOWN1_PANE) {
734: if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
735: *key = KEYC_NONE;
736: return (0);
737: }
738: if (x > mtd->width || y > mtd->height) {
739: *key = KEYC_NONE;
740: return (0);
741: }
742: if (mtd->offset + y < mtd->line_size) {
743: mtd->current = mtd->offset + y;
744: *key = '\r';
745: return (0);
746: }
747: }
748:
749: line = &mtd->line_list[mtd->current];
750: current = line->item;
751:
752: choice = -1;
753: if (*key >= '0' && *key <= '9')
754: choice = (*key) - '0';
755: else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
756: tmp = (*key) & KEYC_MASK_KEY;
757: if (tmp >= 'a' && tmp <= 'z')
758: choice = 10 + (tmp - 'a');
759: }
760: if (choice != -1) {
761: if ((u_int)choice > mtd->line_size - 1) {
762: *key = KEYC_NONE;
763: return (0);
764: }
765: mtd->current = choice;
766: *key = '\r';
767: return (0);
768: }
769:
770: switch (*key) {
771: case 'q':
772: case '\033': /* Escape */
773: return (1);
774: case KEYC_UP:
775: case 'k':
776: case KEYC_WHEELUP_PANE:
777: mode_tree_up(mtd, 1);
778: break;
779: case KEYC_DOWN:
780: case 'j':
781: case KEYC_WHEELDOWN_PANE:
782: mode_tree_down(mtd, 1);
783: break;
784: case KEYC_PPAGE:
785: case '\002': /* C-b */
786: for (i = 0; i < mtd->height; i++) {
787: if (mtd->current == 0)
788: break;
789: mode_tree_up(mtd, 1);
790: }
791: break;
792: case KEYC_NPAGE:
793: case '\006': /* C-f */
794: for (i = 0; i < mtd->height; i++) {
795: if (mtd->current == mtd->line_size - 1)
796: break;
797: mode_tree_down(mtd, 1);
798: }
799: break;
800: case KEYC_HOME:
801: mtd->current = 0;
802: mtd->offset = 0;
803: break;
804: case KEYC_END:
805: mtd->current = mtd->line_size - 1;
806: if (mtd->current > mtd->height - 1)
1.11 ! nicm 807: mtd->offset = mtd->current - mtd->height + 1;
1.1 nicm 808: else
809: mtd->offset = 0;
810: break;
811: case 't':
812: /*
813: * Do not allow parents and children to both be tagged: untag
814: * all parents and children of current.
815: */
816: if (!current->tagged) {
817: parent = current->parent;
818: while (parent != NULL) {
819: parent->tagged = 0;
820: parent = parent->parent;
821: }
822: mode_tree_clear_tagged(¤t->children);
823: current->tagged = 1;
824: } else
825: current->tagged = 0;
826: mode_tree_down(mtd, 0);
827: break;
828: case 'T':
829: for (i = 0; i < mtd->line_size; i++)
830: mtd->line_list[i].item->tagged = 0;
831: break;
832: case '\024': /* C-t */
833: for (i = 0; i < mtd->line_size; i++) {
834: if (mtd->line_list[i].item->parent == NULL)
835: mtd->line_list[i].item->tagged = 1;
836: else
837: mtd->line_list[i].item->tagged = 0;
838: }
839: break;
840: case 'O':
841: mtd->sort_type++;
842: if (mtd->sort_type == mtd->sort_size)
843: mtd->sort_type = 0;
844: mode_tree_build(mtd);
845: break;
846: case KEYC_LEFT:
847: case '-':
848: if (line->flat || !current->expanded)
849: current = current->parent;
850: if (current == NULL)
851: mode_tree_up(mtd, 0);
852: else {
853: current->expanded = 0;
854: mtd->current = current->line;
855: mode_tree_build(mtd);
856: }
857: break;
858: case KEYC_RIGHT:
859: case '+':
860: if (line->flat || current->expanded)
861: mode_tree_down(mtd, 0);
862: else if (!line->flat) {
863: current->expanded = 1;
864: mode_tree_build(mtd);
865: }
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: }