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