Annotation of src/usr.bin/tmux/mode-tree.c, Revision 1.4
1.4 ! nicm 1: /* $OpenBSD: mode-tree.c,v 1.3 2017/06/07 14:37:30 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 *
276: mode_tree_start(struct window_pane *wp, void (*buildcb)(void *, u_int,
277: uint64_t *), struct screen *(*drawcb)(void *, void *, u_int, u_int),
1.3 nicm 278: int (*searchcb)(void *, void *, const char *), void *modedata,
279: const char **sort_list, u_int sort_size, struct screen **s)
1.1 nicm 280: {
281: struct mode_tree_data *mtd;
282:
283: mtd = xcalloc(1, sizeof *mtd);
1.3 nicm 284: mtd->references = 1;
285:
1.1 nicm 286: mtd->wp = wp;
287: mtd->modedata = modedata;
288:
289: mtd->sort_list = sort_list;
290: mtd->sort_size = sort_size;
291: mtd->sort_type = 0;
292:
293: mtd->buildcb = buildcb;
294: mtd->drawcb = drawcb;
1.3 nicm 295: mtd->searchcb = searchcb;
1.1 nicm 296:
297: TAILQ_INIT(&mtd->children);
298:
299: *s = &mtd->screen;
300: screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
301: (*s)->mode &= ~MODE_CURSOR;
302:
303: return (mtd);
304: }
305:
306: void
307: mode_tree_build(struct mode_tree_data *mtd)
308: {
309: struct screen *s = &mtd->screen;
310: uint64_t tag;
311:
312: if (mtd->line_list != NULL)
313: tag = mtd->line_list[mtd->current].item->tag;
314: else
315: tag = 0;
316:
317: TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
318: TAILQ_INIT(&mtd->children);
319:
320: mtd->buildcb(mtd->modedata, mtd->sort_type, &tag);
321:
322: mode_tree_free_items(&mtd->saved);
323: TAILQ_INIT(&mtd->saved);
324:
325: mode_tree_clear_lines(mtd);
326: mode_tree_build_lines(mtd, &mtd->children, 0);
327:
1.3 nicm 328: mode_tree_set_current(mtd, tag);
1.1 nicm 329:
330: mtd->width = screen_size_x(s);
331: mtd->height = (screen_size_y(s) / 3) * 2;
332: if (mtd->height > mtd->line_size)
333: mtd->height = screen_size_y(s) / 2;
334: if (mtd->height < 10)
335: mtd->height = screen_size_y(s);
336: if (screen_size_y(s) - mtd->height < 2)
337: mtd->height = screen_size_y(s);
338: }
339:
1.3 nicm 340: static void
341: mode_tree_remove_ref(struct mode_tree_data *mtd)
342: {
343: if (--mtd->references == 0)
344: free(mtd);
345: }
346:
1.1 nicm 347: void
348: mode_tree_free(struct mode_tree_data *mtd)
349: {
350: mode_tree_free_items(&mtd->children);
351: mode_tree_clear_lines(mtd);
352: screen_free(&mtd->screen);
1.3 nicm 353:
354: mtd->dead = 1;
355: mode_tree_remove_ref(mtd);
1.1 nicm 356: }
357:
358: void
359: mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
360: {
361: struct screen *s = &mtd->screen;
362:
363: screen_resize(s, sx, sy, 0);
364:
365: mode_tree_build(mtd);
366: mode_tree_draw(mtd);
367:
368: mtd->wp->flags |= PANE_REDRAW;
369: }
370:
371: struct mode_tree_item *
372: mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
373: void *itemdata, uint64_t tag, const char *name, const char *text,
374: int expanded)
375: {
376: struct mode_tree_item *mti, *saved;
377:
378: log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
379: name, text);
380:
381: mti = xcalloc(1, sizeof *mti);
382: mti->parent = parent;
383: mti->itemdata = itemdata;
384:
385: mti->tag = tag;
386: mti->name = xstrdup(name);
387: mti->text = xstrdup(text);
388:
389: saved = mode_tree_find_item(&mtd->saved, tag);
390: if (saved != NULL) {
391: if (parent == NULL || (parent != NULL && parent->expanded))
392: mti->tagged = saved->tagged;
393: mti->expanded = saved->expanded;
394: } else if (expanded == -1)
395: mti->expanded = 1;
396: else
397: mti->expanded = expanded;
398:
399: TAILQ_INIT(&mti->children);
400:
401: if (parent != NULL)
402: TAILQ_INSERT_TAIL(&parent->children, mti, entry);
403: else
404: TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
405:
406: return (mti);
407: }
408:
409: void
410: mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
411: {
412: struct mode_tree_item *parent = mti->parent;
413:
414: if (parent != NULL)
415: TAILQ_REMOVE(&parent->children, mti, entry);
416: else
417: TAILQ_REMOVE(&mtd->children, mti, entry);
418: mode_tree_free_item(mti);
419: }
420:
421: void
422: mode_tree_draw(struct mode_tree_data *mtd)
423: {
424: struct window_pane *wp = mtd->wp;
425: struct screen *s = &mtd->screen, *box;
426: struct mode_tree_line *line;
427: struct mode_tree_item *mti;
428: struct options *oo = wp->window->options;
429: struct screen_write_ctx ctx;
430: struct grid_cell gc0, gc;
431: u_int w, h, i, j, sy, box_x, box_y;
432: char *text, *start, key[7];
433: const char *tag, *symbol;
434: size_t size;
435: int keylen;
436:
437: if (mtd->line_size == 0)
438: return;
439:
440: memcpy(&gc0, &grid_default_cell, sizeof gc0);
441: memcpy(&gc, &grid_default_cell, sizeof gc);
442: style_apply(&gc, oo, "mode-style");
443:
444: w = mtd->width;
445: h = mtd->height;
446:
447: screen_write_start(&ctx, NULL, s);
448: screen_write_clearscreen(&ctx, 8);
449:
450: if (mtd->line_size > 10)
451: keylen = 6;
452: else
453: keylen = 4;
454:
455: for (i = 0; i < mtd->line_size; i++) {
456: if (i < mtd->offset)
457: continue;
458: if (i > mtd->offset + h - 1)
459: break;
460:
461: line = &mtd->line_list[i];
462: mti = line->item;
463:
464: screen_write_cursormove(&ctx, 0, i - mtd->offset);
465:
466: if (i < 10)
467: snprintf(key, sizeof key, "(%c)", '0' + i);
468: else if (i < 36)
469: snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
470: else
471: *key = '\0';
472:
473: if (line->flat)
474: symbol = "";
475: else if (TAILQ_EMPTY(&mti->children))
476: symbol = " ";
477: else if (mti->expanded)
478: symbol = "- ";
479: else
480: symbol = "+ ";
481:
482: if (line->depth == 0)
483: start = xstrdup(symbol);
484: else {
485: size = (4 * line->depth) + 32;
486:
487: start = xcalloc(1, size);
488: for (j = 1; j < line->depth; j++) {
489: if (mti->parent != NULL &&
490: mtd->line_list[mti->parent->line].last)
491: strlcat(start, " ", size);
492: else
493: strlcat(start, "\001x\001 ", size);
494: }
495: if (line->last)
496: strlcat(start, "\001mq\001> ", size);
497: else
498: strlcat(start, "\001tq\001> ", size);
499: strlcat(start, symbol, size);
500: }
501:
502: if (mti->tagged)
503: tag = "*";
504: else
505: tag = "";
506: xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start,
507: mti->name, tag, mti->text);
508: free(start);
509:
510: if (mti->tagged) {
511: gc.attr ^= GRID_ATTR_BRIGHT;
512: gc0.attr ^= GRID_ATTR_BRIGHT;
513: }
514:
515: if (i != mtd->current) {
516: screen_write_puts(&ctx, &gc0, "%.*s", w, text);
517: screen_write_clearendofline(&ctx, 8);
518: } else
519: screen_write_puts(&ctx, &gc, "%-*.*s", w, w, text);
520: free(text);
521:
522: if (mti->tagged) {
523: gc.attr ^= GRID_ATTR_BRIGHT;
524: gc0.attr ^= GRID_ATTR_BRIGHT;
525: }
526: }
527:
528: sy = screen_size_y(s);
529: if (sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
530: screen_write_stop(&ctx);
531: return;
532: }
533:
534: line = &mtd->line_list[mtd->current];
535: mti = line->item;
536:
537: screen_write_cursormove(&ctx, 0, h);
538: screen_write_box(&ctx, w, sy - h);
539:
540: xasprintf(&text, " %s (sort: %s) ", mti->name,
541: mtd->sort_list[mtd->sort_type]);
542: if (w - 2 >= strlen(text)) {
543: screen_write_cursormove(&ctx, 1, h);
544: screen_write_puts(&ctx, &gc0, "%s", text);
545: }
546: free(text);
547:
548: box_x = w - 4;
549: box_y = sy - h - 2;
550:
551: box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y);
552: if (box != NULL) {
553: screen_write_cursormove(&ctx, 2, h + 1);
554: screen_write_copy(&ctx, box, 0, 0, box_x, box_y, NULL, NULL);
555:
556: screen_free(box);
557: }
558:
559: screen_write_stop(&ctx);
560: }
561:
1.3 nicm 562: static struct mode_tree_item *
563: mode_tree_search_for(struct mode_tree_data *mtd)
564: {
565: struct mode_tree_item *mti, *last, *next;
566:
567: if (mtd->ss == NULL)
568: return (NULL);
569:
570: mti = last = mtd->line_list[mtd->current].item;
571: for (;;) {
572: if (!TAILQ_EMPTY(&mti->children))
573: mti = TAILQ_FIRST(&mti->children);
574: else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
575: mti = next;
576: else {
577: for (;;) {
578: mti = mti->parent;
579: if (mti == NULL)
580: break;
581: if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
582: mti = next;
583: break;
584: }
585: }
586: }
587: if (mti == NULL)
588: mti = TAILQ_FIRST(&mtd->children);
589: if (mti == last)
590: break;
591:
592: if (mtd->searchcb == NULL) {
593: if (strstr(mti->name, mtd->ss) != NULL)
594: return (mti);
595: continue;
596: }
597: if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->ss))
1.4 ! nicm 598: return (mti);
1.3 nicm 599: }
600: return (NULL);
601: }
602:
603: static void
604: mode_tree_search_set(struct mode_tree_data *mtd)
605: {
606: struct mode_tree_item *mti, *loop;
607: uint64_t tag;
608:
609: mti = mode_tree_search_for(mtd);
610: if (mti == NULL)
611: return;
612: tag = mti->tag;
613:
614: loop = mti->parent;
615: while (loop != NULL) {
616: loop->expanded = 1;
617: loop = loop->parent;
618: }
619: mode_tree_build(mtd);
620:
621: mode_tree_set_current(mtd, tag);
622: mode_tree_draw(mtd);
623: }
624:
625: static int
626: mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
627: __unused int done)
628: {
629: struct mode_tree_data *mtd = data;
630:
631: if (mtd->dead)
632: return (0);
633:
634: free(mtd->ss);
635: if (*s == '\0') {
636: mtd->ss = NULL;
637: return (0);
638: }
639: mtd->ss = xstrdup(s);
640: mode_tree_search_set(mtd);
641:
642: return (0);
643: }
644:
645: static void
646: mode_tree_search_free(void *data)
647: {
648: mode_tree_remove_ref(data);
649: }
650:
1.1 nicm 651: int
1.3 nicm 652: mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
653: struct mouse_event *m)
1.1 nicm 654: {
655: struct mode_tree_line *line;
656: struct mode_tree_item *current, *parent;
657: u_int i, x, y;
658: int choice;
659: key_code tmp;
660:
661: if (*key == KEYC_MOUSEDOWN1_PANE) {
662: if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
663: *key = KEYC_NONE;
664: return (0);
665: }
666: if (x > mtd->width || y > mtd->height) {
667: *key = KEYC_NONE;
668: return (0);
669: }
670: if (mtd->offset + y < mtd->line_size) {
671: mtd->current = mtd->offset + y;
672: *key = '\r';
673: return (0);
674: }
675: }
676:
677: line = &mtd->line_list[mtd->current];
678: current = line->item;
679:
680: choice = -1;
681: if (*key >= '0' && *key <= '9')
682: choice = (*key) - '0';
683: else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
684: tmp = (*key) & KEYC_MASK_KEY;
685: if (tmp >= 'a' && tmp <= 'z')
686: choice = 10 + (tmp - 'a');
687: }
688: if (choice != -1) {
689: if ((u_int)choice > mtd->line_size - 1) {
690: *key = KEYC_NONE;
691: return (0);
692: }
693: mtd->current = choice;
694: *key = '\r';
695: return (0);
696: }
697:
698: switch (*key) {
699: case 'q':
700: case '\033': /* Escape */
701: return (1);
702: case KEYC_UP:
703: case 'k':
704: case KEYC_WHEELUP_PANE:
705: mode_tree_up(mtd, 1);
706: break;
707: case KEYC_DOWN:
708: case 'j':
709: case KEYC_WHEELDOWN_PANE:
710: mode_tree_down(mtd, 1);
711: break;
712: case KEYC_PPAGE:
713: case '\002': /* C-b */
714: for (i = 0; i < mtd->height; i++) {
715: if (mtd->current == 0)
716: break;
717: mode_tree_up(mtd, 1);
718: }
719: break;
720: case KEYC_NPAGE:
721: case '\006': /* C-f */
722: for (i = 0; i < mtd->height; i++) {
723: if (mtd->current == mtd->line_size - 1)
724: break;
725: mode_tree_down(mtd, 1);
726: }
727: break;
728: case KEYC_HOME:
729: mtd->current = 0;
730: mtd->offset = 0;
731: break;
732: case KEYC_END:
733: mtd->current = mtd->line_size - 1;
734: if (mtd->current > mtd->height - 1)
735: mtd->offset = mtd->current - mtd->height;
736: else
737: mtd->offset = 0;
738: break;
739: case 't':
740: /*
741: * Do not allow parents and children to both be tagged: untag
742: * all parents and children of current.
743: */
744: if (!current->tagged) {
745: parent = current->parent;
746: while (parent != NULL) {
747: parent->tagged = 0;
748: parent = parent->parent;
749: }
750: mode_tree_clear_tagged(¤t->children);
751: current->tagged = 1;
752: } else
753: current->tagged = 0;
754: mode_tree_down(mtd, 0);
755: break;
756: case 'T':
757: for (i = 0; i < mtd->line_size; i++)
758: mtd->line_list[i].item->tagged = 0;
759: break;
760: case '\024': /* C-t */
761: for (i = 0; i < mtd->line_size; i++) {
762: if (mtd->line_list[i].item->parent == NULL)
763: mtd->line_list[i].item->tagged = 1;
764: else
765: mtd->line_list[i].item->tagged = 0;
766: }
767: break;
768: case 'O':
769: mtd->sort_type++;
770: if (mtd->sort_type == mtd->sort_size)
771: mtd->sort_type = 0;
772: mode_tree_build(mtd);
773: break;
774: case KEYC_LEFT:
775: case '-':
776: if (line->flat || !current->expanded)
777: current = current->parent;
778: if (current == NULL)
779: mode_tree_up(mtd, 0);
780: else {
781: current->expanded = 0;
782: mtd->current = current->line;
783: mode_tree_build(mtd);
784: }
785: break;
786: case KEYC_RIGHT:
787: case '+':
788: if (line->flat || current->expanded)
789: mode_tree_down(mtd, 0);
790: else if (!line->flat) {
791: current->expanded = 1;
792: mode_tree_build(mtd);
793: }
1.3 nicm 794: break;
795: case '\023': /* C-s */
796: mtd->references++;
797: status_prompt_set(c, "(search) ", "",
798: mode_tree_search_callback, mode_tree_search_free, mtd,
799: PROMPT_NOFORMAT);
800: break;
801: case 'n':
802: mode_tree_search_set(mtd);
1.1 nicm 803: break;
804: }
805: return (0);
806: }
807:
808: void
809: mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
810: const char *template, const char *name)
811: {
812: struct cmdq_item *new_item;
813: struct cmd_list *cmdlist;
814: char *command, *cause;
815:
816: command = cmd_template_replace(template, name, 1);
1.2 nicm 817: if (command == NULL || *command == '\0') {
818: free(command);
1.1 nicm 819: return;
1.2 nicm 820: }
1.1 nicm 821:
822: cmdlist = cmd_string_parse(command, NULL, 0, &cause);
823: if (cmdlist == NULL) {
824: if (cause != NULL && c != NULL) {
825: *cause = toupper((u_char)*cause);
826: status_message_set(c, "%s", cause);
827: }
828: free(cause);
829: } else {
830: new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
831: cmdq_append(c, new_item);
832: cmd_list_free(cmdlist);
833: }
834:
835: free(command);
836: }