Annotation of src/usr.bin/tmux/mode-tree.c, Revision 1.27
1.27 ! nicm 1: /* $OpenBSD: mode-tree.c,v 1.26 2019/03/12 20:02:47 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;
1.27 ! nicm 500: u_int w, h, i, j, sy, box_x, box_y, width;
1.1 nicm 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:
1.26 nicm 533: screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
1.1 nicm 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 = "";
1.27 ! nicm 575: xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name,
! 576: tag);
! 577: width = utf8_cstrwidth(text);
1.1 nicm 578: free(start);
579:
580: if (mti->tagged) {
581: gc.attr ^= GRID_ATTR_BRIGHT;
582: gc0.attr ^= GRID_ATTR_BRIGHT;
583: }
584:
585: if (i != mtd->current) {
586: screen_write_clearendofline(&ctx, 8);
1.27 ! nicm 587: screen_write_puts(&ctx, &gc0, "%s", text);
! 588: format_draw(&ctx, &gc0, w - width, mti->text, NULL);
1.13 nicm 589: } else {
590: screen_write_clearendofline(&ctx, gc.bg);
1.27 ! nicm 591: screen_write_puts(&ctx, &gc, "%s", text);
! 592: format_draw(&ctx, &gc, w - width, mti->text, NULL);
1.13 nicm 593: }
1.1 nicm 594: free(text);
595:
596: if (mti->tagged) {
597: gc.attr ^= GRID_ATTR_BRIGHT;
598: gc0.attr ^= GRID_ATTR_BRIGHT;
599: }
600: }
601:
602: sy = screen_size_y(s);
1.9 nicm 603: if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
1.1 nicm 604: screen_write_stop(&ctx);
605: return;
606: }
607:
608: line = &mtd->line_list[mtd->current];
609: mti = line->item;
610:
1.26 nicm 611: screen_write_cursormove(&ctx, 0, h, 0);
1.1 nicm 612: screen_write_box(&ctx, w, sy - h);
613:
1.21 nicm 614: xasprintf(&text, " %s (sort: %s)", mti->name,
1.1 nicm 615: mtd->sort_list[mtd->sort_type]);
616: if (w - 2 >= strlen(text)) {
1.26 nicm 617: screen_write_cursormove(&ctx, 1, h, 0);
1.1 nicm 618: screen_write_puts(&ctx, &gc0, "%s", text);
1.21 nicm 619:
620: if (mtd->no_matches)
621: n = (sizeof "no matches") - 1;
622: else
623: n = (sizeof "active") - 1;
624: if (mtd->filter != NULL && w - 2 >= strlen(text) + 10 + n + 2) {
625: screen_write_puts(&ctx, &gc0, " (filter: ");
626: if (mtd->no_matches)
627: screen_write_puts(&ctx, &gc, "no matches");
628: else
629: screen_write_puts(&ctx, &gc0, "active");
630: screen_write_puts(&ctx, &gc0, ") ");
631: }
1.1 nicm 632: }
633: free(text);
634:
635: box_x = w - 4;
636: box_y = sy - h - 2;
637:
1.17 nicm 638: if (box_x != 0 && box_y != 0) {
1.26 nicm 639: screen_write_cursormove(&ctx, 2, h + 1, 0);
1.17 nicm 640: mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y);
1.1 nicm 641: }
642:
643: screen_write_stop(&ctx);
644: }
645:
1.3 nicm 646: static struct mode_tree_item *
647: mode_tree_search_for(struct mode_tree_data *mtd)
648: {
649: struct mode_tree_item *mti, *last, *next;
650:
1.6 nicm 651: if (mtd->search == NULL)
1.3 nicm 652: return (NULL);
653:
654: mti = last = mtd->line_list[mtd->current].item;
655: for (;;) {
656: if (!TAILQ_EMPTY(&mti->children))
657: mti = TAILQ_FIRST(&mti->children);
658: else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
659: mti = next;
660: else {
661: for (;;) {
662: mti = mti->parent;
663: if (mti == NULL)
664: break;
665: if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
666: mti = next;
667: break;
668: }
669: }
670: }
671: if (mti == NULL)
672: mti = TAILQ_FIRST(&mtd->children);
673: if (mti == last)
674: break;
675:
676: if (mtd->searchcb == NULL) {
1.6 nicm 677: if (strstr(mti->name, mtd->search) != NULL)
1.3 nicm 678: return (mti);
679: continue;
680: }
1.6 nicm 681: if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
1.4 nicm 682: return (mti);
1.3 nicm 683: }
684: return (NULL);
685: }
686:
687: static void
688: mode_tree_search_set(struct mode_tree_data *mtd)
689: {
690: struct mode_tree_item *mti, *loop;
691: uint64_t tag;
692:
693: mti = mode_tree_search_for(mtd);
694: if (mti == NULL)
695: return;
696: tag = mti->tag;
697:
698: loop = mti->parent;
699: while (loop != NULL) {
700: loop->expanded = 1;
701: loop = loop->parent;
702: }
1.6 nicm 703:
1.3 nicm 704: mode_tree_build(mtd);
705: mode_tree_set_current(mtd, tag);
706: mode_tree_draw(mtd);
1.6 nicm 707: mtd->wp->flags |= PANE_REDRAW;
1.3 nicm 708: }
709:
710: static int
711: mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
712: __unused int done)
713: {
714: struct mode_tree_data *mtd = data;
715:
716: if (mtd->dead)
717: return (0);
718:
1.6 nicm 719: free(mtd->search);
720: if (s == NULL || *s == '\0') {
721: mtd->search = NULL;
1.3 nicm 722: return (0);
723: }
1.6 nicm 724: mtd->search = xstrdup(s);
1.3 nicm 725: mode_tree_search_set(mtd);
726:
727: return (0);
728: }
729:
730: static void
731: mode_tree_search_free(void *data)
732: {
733: mode_tree_remove_ref(data);
734: }
735:
1.6 nicm 736: static int
737: mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
738: __unused int done)
739: {
740: struct mode_tree_data *mtd = data;
741:
742: if (mtd->dead)
743: return (0);
744:
745: if (mtd->filter != NULL)
746: free(mtd->filter);
747: if (s == NULL || *s == '\0')
748: mtd->filter = NULL;
749: else
750: mtd->filter = xstrdup(s);
751:
752: mode_tree_build(mtd);
753: mode_tree_draw(mtd);
754: mtd->wp->flags |= PANE_REDRAW;
755:
756: return (0);
757: }
758:
759: static void
760: mode_tree_filter_free(void *data)
761: {
762: mode_tree_remove_ref(data);
763: }
764:
1.1 nicm 765: int
1.3 nicm 766: mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
1.19 nicm 767: struct mouse_event *m, u_int *xp, u_int *yp)
1.1 nicm 768: {
769: struct mode_tree_line *line;
770: struct mode_tree_item *current, *parent;
771: u_int i, x, y;
772: int choice;
773: key_code tmp;
774:
1.18 nicm 775: if (KEYC_IS_MOUSE(*key)) {
1.1 nicm 776: if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
777: *key = KEYC_NONE;
778: return (0);
779: }
1.19 nicm 780: if (xp != NULL)
781: *xp = x;
782: if (yp != NULL)
783: *yp = y;
1.1 nicm 784: if (x > mtd->width || y > mtd->height) {
1.20 nicm 785: if (!mtd->preview)
1.19 nicm 786: *key = KEYC_NONE;
1.1 nicm 787: return (0);
788: }
789: if (mtd->offset + y < mtd->line_size) {
1.18 nicm 790: if (*key == KEYC_MOUSEDOWN1_PANE ||
791: *key == KEYC_DOUBLECLICK1_PANE)
792: mtd->current = mtd->offset + y;
793: if (*key == KEYC_DOUBLECLICK1_PANE)
794: *key = '\r';
1.20 nicm 795: else
796: *key = KEYC_NONE;
797: } else
798: *key = KEYC_NONE;
799: return (0);
1.1 nicm 800: }
801:
802: line = &mtd->line_list[mtd->current];
803: current = line->item;
804:
805: choice = -1;
806: if (*key >= '0' && *key <= '9')
807: choice = (*key) - '0';
808: else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
809: tmp = (*key) & KEYC_MASK_KEY;
810: if (tmp >= 'a' && tmp <= 'z')
811: choice = 10 + (tmp - 'a');
812: }
813: if (choice != -1) {
814: if ((u_int)choice > mtd->line_size - 1) {
815: *key = KEYC_NONE;
816: return (0);
817: }
818: mtd->current = choice;
819: *key = '\r';
820: return (0);
821: }
822:
823: switch (*key) {
824: case 'q':
825: case '\033': /* Escape */
1.22 nicm 826: case '\007': /* C-g */
1.1 nicm 827: return (1);
828: case KEYC_UP:
829: case 'k':
830: case KEYC_WHEELUP_PANE:
1.12 nicm 831: case '\020': /* C-p */
1.1 nicm 832: mode_tree_up(mtd, 1);
833: break;
834: case KEYC_DOWN:
835: case 'j':
836: case KEYC_WHEELDOWN_PANE:
1.12 nicm 837: case '\016': /* C-n */
1.1 nicm 838: mode_tree_down(mtd, 1);
839: break;
840: case KEYC_PPAGE:
841: case '\002': /* C-b */
842: for (i = 0; i < mtd->height; i++) {
843: if (mtd->current == 0)
844: break;
845: mode_tree_up(mtd, 1);
846: }
847: break;
848: case KEYC_NPAGE:
849: case '\006': /* C-f */
850: for (i = 0; i < mtd->height; i++) {
851: if (mtd->current == mtd->line_size - 1)
852: break;
853: mode_tree_down(mtd, 1);
854: }
855: break;
856: case KEYC_HOME:
857: mtd->current = 0;
858: mtd->offset = 0;
859: break;
860: case KEYC_END:
861: mtd->current = mtd->line_size - 1;
862: if (mtd->current > mtd->height - 1)
1.11 nicm 863: mtd->offset = mtd->current - mtd->height + 1;
1.1 nicm 864: else
865: mtd->offset = 0;
866: break;
867: case 't':
868: /*
869: * Do not allow parents and children to both be tagged: untag
870: * all parents and children of current.
871: */
872: if (!current->tagged) {
873: parent = current->parent;
874: while (parent != NULL) {
875: parent->tagged = 0;
876: parent = parent->parent;
877: }
878: mode_tree_clear_tagged(¤t->children);
879: current->tagged = 1;
880: } else
881: current->tagged = 0;
882: mode_tree_down(mtd, 0);
883: break;
884: case 'T':
885: for (i = 0; i < mtd->line_size; i++)
886: mtd->line_list[i].item->tagged = 0;
887: break;
888: case '\024': /* C-t */
889: for (i = 0; i < mtd->line_size; i++) {
890: if (mtd->line_list[i].item->parent == NULL)
891: mtd->line_list[i].item->tagged = 1;
892: else
893: mtd->line_list[i].item->tagged = 0;
894: }
895: break;
896: case 'O':
897: mtd->sort_type++;
898: if (mtd->sort_type == mtd->sort_size)
899: mtd->sort_type = 0;
900: mode_tree_build(mtd);
901: break;
902: case KEYC_LEFT:
1.15 nicm 903: case 'h':
1.1 nicm 904: case '-':
905: if (line->flat || !current->expanded)
906: current = current->parent;
907: if (current == NULL)
908: mode_tree_up(mtd, 0);
909: else {
910: current->expanded = 0;
911: mtd->current = current->line;
912: mode_tree_build(mtd);
913: }
914: break;
915: case KEYC_RIGHT:
1.15 nicm 916: case 'l':
1.1 nicm 917: case '+':
918: if (line->flat || current->expanded)
919: mode_tree_down(mtd, 0);
920: else if (!line->flat) {
921: current->expanded = 1;
922: mode_tree_build(mtd);
923: }
1.3 nicm 924: break;
925: case '\023': /* C-s */
926: mtd->references++;
927: status_prompt_set(c, "(search) ", "",
928: mode_tree_search_callback, mode_tree_search_free, mtd,
929: PROMPT_NOFORMAT);
930: break;
931: case 'n':
932: mode_tree_search_set(mtd);
1.6 nicm 933: break;
934: case 'f':
935: mtd->references++;
936: status_prompt_set(c, "(filter) ", mtd->filter,
937: mode_tree_filter_callback, mode_tree_filter_free, mtd,
938: PROMPT_NOFORMAT);
1.9 nicm 939: break;
940: case 'v':
941: mtd->preview = !mtd->preview;
942: mode_tree_build(mtd);
1.11 nicm 943: if (mtd->preview)
944: mode_tree_check_selected(mtd);
1.1 nicm 945: break;
946: }
947: return (0);
948: }
949:
950: void
951: mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
952: const char *template, const char *name)
953: {
954: struct cmdq_item *new_item;
955: struct cmd_list *cmdlist;
956: char *command, *cause;
957:
958: command = cmd_template_replace(template, name, 1);
1.2 nicm 959: if (command == NULL || *command == '\0') {
960: free(command);
1.1 nicm 961: return;
1.2 nicm 962: }
1.1 nicm 963:
964: cmdlist = cmd_string_parse(command, NULL, 0, &cause);
965: if (cmdlist == NULL) {
966: if (cause != NULL && c != NULL) {
967: *cause = toupper((u_char)*cause);
968: status_message_set(c, "%s", cause);
969: }
970: free(cause);
971: } else {
972: new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
973: cmdq_append(c, new_item);
974: cmd_list_free(cmdlist);
975: }
976:
977: free(command);
978: }