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