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