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