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