Annotation of src/usr.bin/tmux/mode-tree.c, Revision 1.6
1.6 ! nicm 1: /* $OpenBSD: mode-tree.c,v 1.5 2017/06/09 15:29:15 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;
448: struct screen *s = &mtd->screen, *box;
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:
574: box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y);
575: if (box != NULL) {
576: screen_write_cursormove(&ctx, 2, h + 1);
577: screen_write_copy(&ctx, box, 0, 0, box_x, box_y, NULL, NULL);
578:
579: screen_free(box);
580: }
581:
582: screen_write_stop(&ctx);
583: }
584:
1.3 nicm 585: static struct mode_tree_item *
586: mode_tree_search_for(struct mode_tree_data *mtd)
587: {
588: struct mode_tree_item *mti, *last, *next;
589:
1.6 ! nicm 590: if (mtd->search == NULL)
1.3 nicm 591: return (NULL);
592:
593: mti = last = mtd->line_list[mtd->current].item;
594: for (;;) {
595: if (!TAILQ_EMPTY(&mti->children))
596: mti = TAILQ_FIRST(&mti->children);
597: else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
598: mti = next;
599: else {
600: for (;;) {
601: mti = mti->parent;
602: if (mti == NULL)
603: break;
604: if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
605: mti = next;
606: break;
607: }
608: }
609: }
610: if (mti == NULL)
611: mti = TAILQ_FIRST(&mtd->children);
612: if (mti == last)
613: break;
614:
615: if (mtd->searchcb == NULL) {
1.6 ! nicm 616: if (strstr(mti->name, mtd->search) != NULL)
1.3 nicm 617: return (mti);
618: continue;
619: }
1.6 ! nicm 620: if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
1.4 nicm 621: return (mti);
1.3 nicm 622: }
623: return (NULL);
624: }
625:
626: static void
627: mode_tree_search_set(struct mode_tree_data *mtd)
628: {
629: struct mode_tree_item *mti, *loop;
630: uint64_t tag;
631:
632: mti = mode_tree_search_for(mtd);
633: if (mti == NULL)
634: return;
635: tag = mti->tag;
636:
637: loop = mti->parent;
638: while (loop != NULL) {
639: loop->expanded = 1;
640: loop = loop->parent;
641: }
1.6 ! nicm 642:
1.3 nicm 643: mode_tree_build(mtd);
644: mode_tree_set_current(mtd, tag);
645: mode_tree_draw(mtd);
1.6 ! nicm 646: mtd->wp->flags |= PANE_REDRAW;
1.3 nicm 647: }
648:
649: static int
650: mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
651: __unused int done)
652: {
653: struct mode_tree_data *mtd = data;
654:
655: if (mtd->dead)
656: return (0);
657:
1.6 ! nicm 658: free(mtd->search);
! 659: if (s == NULL || *s == '\0') {
! 660: mtd->search = NULL;
1.3 nicm 661: return (0);
662: }
1.6 ! nicm 663: mtd->search = xstrdup(s);
1.3 nicm 664: mode_tree_search_set(mtd);
665:
666: return (0);
667: }
668:
669: static void
670: mode_tree_search_free(void *data)
671: {
672: mode_tree_remove_ref(data);
673: }
674:
1.6 ! nicm 675: static int
! 676: mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
! 677: __unused int done)
! 678: {
! 679: struct mode_tree_data *mtd = data;
! 680:
! 681: if (mtd->dead)
! 682: return (0);
! 683:
! 684: if (mtd->filter != NULL)
! 685: free(mtd->filter);
! 686: if (s == NULL || *s == '\0')
! 687: mtd->filter = NULL;
! 688: else
! 689: mtd->filter = xstrdup(s);
! 690:
! 691: mode_tree_build(mtd);
! 692: mode_tree_draw(mtd);
! 693: mtd->wp->flags |= PANE_REDRAW;
! 694:
! 695: return (0);
! 696: }
! 697:
! 698: static void
! 699: mode_tree_filter_free(void *data)
! 700: {
! 701: mode_tree_remove_ref(data);
! 702: }
! 703:
1.1 nicm 704: int
1.3 nicm 705: mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
706: struct mouse_event *m)
1.1 nicm 707: {
708: struct mode_tree_line *line;
709: struct mode_tree_item *current, *parent;
710: u_int i, x, y;
711: int choice;
712: key_code tmp;
713:
714: if (*key == KEYC_MOUSEDOWN1_PANE) {
715: if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
716: *key = KEYC_NONE;
717: return (0);
718: }
719: if (x > mtd->width || y > mtd->height) {
720: *key = KEYC_NONE;
721: return (0);
722: }
723: if (mtd->offset + y < mtd->line_size) {
724: mtd->current = mtd->offset + y;
725: *key = '\r';
726: return (0);
727: }
728: }
729:
730: line = &mtd->line_list[mtd->current];
731: current = line->item;
732:
733: choice = -1;
734: if (*key >= '0' && *key <= '9')
735: choice = (*key) - '0';
736: else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
737: tmp = (*key) & KEYC_MASK_KEY;
738: if (tmp >= 'a' && tmp <= 'z')
739: choice = 10 + (tmp - 'a');
740: }
741: if (choice != -1) {
742: if ((u_int)choice > mtd->line_size - 1) {
743: *key = KEYC_NONE;
744: return (0);
745: }
746: mtd->current = choice;
747: *key = '\r';
748: return (0);
749: }
750:
751: switch (*key) {
752: case 'q':
753: case '\033': /* Escape */
754: return (1);
755: case KEYC_UP:
756: case 'k':
757: case KEYC_WHEELUP_PANE:
758: mode_tree_up(mtd, 1);
759: break;
760: case KEYC_DOWN:
761: case 'j':
762: case KEYC_WHEELDOWN_PANE:
763: mode_tree_down(mtd, 1);
764: break;
765: case KEYC_PPAGE:
766: case '\002': /* C-b */
767: for (i = 0; i < mtd->height; i++) {
768: if (mtd->current == 0)
769: break;
770: mode_tree_up(mtd, 1);
771: }
772: break;
773: case KEYC_NPAGE:
774: case '\006': /* C-f */
775: for (i = 0; i < mtd->height; i++) {
776: if (mtd->current == mtd->line_size - 1)
777: break;
778: mode_tree_down(mtd, 1);
779: }
780: break;
781: case KEYC_HOME:
782: mtd->current = 0;
783: mtd->offset = 0;
784: break;
785: case KEYC_END:
786: mtd->current = mtd->line_size - 1;
787: if (mtd->current > mtd->height - 1)
788: mtd->offset = mtd->current - mtd->height;
789: else
790: mtd->offset = 0;
791: break;
792: case 't':
793: /*
794: * Do not allow parents and children to both be tagged: untag
795: * all parents and children of current.
796: */
797: if (!current->tagged) {
798: parent = current->parent;
799: while (parent != NULL) {
800: parent->tagged = 0;
801: parent = parent->parent;
802: }
803: mode_tree_clear_tagged(¤t->children);
804: current->tagged = 1;
805: } else
806: current->tagged = 0;
807: mode_tree_down(mtd, 0);
808: break;
809: case 'T':
810: for (i = 0; i < mtd->line_size; i++)
811: mtd->line_list[i].item->tagged = 0;
812: break;
813: case '\024': /* C-t */
814: for (i = 0; i < mtd->line_size; i++) {
815: if (mtd->line_list[i].item->parent == NULL)
816: mtd->line_list[i].item->tagged = 1;
817: else
818: mtd->line_list[i].item->tagged = 0;
819: }
820: break;
821: case 'O':
822: mtd->sort_type++;
823: if (mtd->sort_type == mtd->sort_size)
824: mtd->sort_type = 0;
825: mode_tree_build(mtd);
826: break;
827: case KEYC_LEFT:
828: case '-':
829: if (line->flat || !current->expanded)
830: current = current->parent;
831: if (current == NULL)
832: mode_tree_up(mtd, 0);
833: else {
834: current->expanded = 0;
835: mtd->current = current->line;
836: mode_tree_build(mtd);
837: }
838: break;
839: case KEYC_RIGHT:
840: case '+':
841: if (line->flat || current->expanded)
842: mode_tree_down(mtd, 0);
843: else if (!line->flat) {
844: current->expanded = 1;
845: mode_tree_build(mtd);
846: }
1.3 nicm 847: break;
848: case '\023': /* C-s */
849: mtd->references++;
850: status_prompt_set(c, "(search) ", "",
851: mode_tree_search_callback, mode_tree_search_free, mtd,
852: PROMPT_NOFORMAT);
853: break;
854: case 'n':
855: mode_tree_search_set(mtd);
1.6 ! nicm 856: break;
! 857: case 'f':
! 858: mtd->references++;
! 859: status_prompt_set(c, "(filter) ", mtd->filter,
! 860: mode_tree_filter_callback, mode_tree_filter_free, mtd,
! 861: PROMPT_NOFORMAT);
1.1 nicm 862: break;
863: }
864: return (0);
865: }
866:
867: void
868: mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
869: const char *template, const char *name)
870: {
871: struct cmdq_item *new_item;
872: struct cmd_list *cmdlist;
873: char *command, *cause;
874:
875: command = cmd_template_replace(template, name, 1);
1.2 nicm 876: if (command == NULL || *command == '\0') {
877: free(command);
1.1 nicm 878: return;
1.2 nicm 879: }
1.1 nicm 880:
881: cmdlist = cmd_string_parse(command, NULL, 0, &cause);
882: if (cmdlist == NULL) {
883: if (cause != NULL && c != NULL) {
884: *cause = toupper((u_char)*cause);
885: status_message_set(c, "%s", cause);
886: }
887: free(cause);
888: } else {
889: new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
890: cmdq_append(c, new_item);
891: cmd_list_free(cmdlist);
892: }
893:
894: free(command);
895: }