4 * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
28 struct mode_tree_item
;
29 TAILQ_HEAD(mode_tree_list
, mode_tree_item
);
31 struct mode_tree_data
{
36 struct window_pane
*wp
;
38 const struct menu_item
*menu
;
40 const char **sort_list
;
42 struct mode_tree_sort_criteria sort_crit
;
44 mode_tree_build_cb buildcb
;
45 mode_tree_draw_cb drawcb
;
46 mode_tree_search_cb searchcb
;
47 mode_tree_menu_cb menucb
;
48 mode_tree_height_cb heightcb
;
49 mode_tree_key_cb keycb
;
51 struct mode_tree_list children
;
52 struct mode_tree_list saved
;
54 struct mode_tree_line
*line_list
;
73 struct mode_tree_item
{
74 struct mode_tree_item
*parent
;
92 struct mode_tree_list children
;
93 TAILQ_ENTRY(mode_tree_item
) entry
;
96 struct mode_tree_line
{
97 struct mode_tree_item
*item
;
103 struct mode_tree_menu
{
104 struct mode_tree_data
*data
;
110 static void mode_tree_free_items(struct mode_tree_list
*);
112 static const struct menu_item mode_tree_menu_items
[] = {
113 { "Scroll Left", '<', NULL
},
114 { "Scroll Right", '>', NULL
},
115 { "", KEYC_NONE
, NULL
},
116 { "Cancel", 'q', NULL
},
118 { NULL
, KEYC_NONE
, NULL
}
121 static struct mode_tree_item
*
122 mode_tree_find_item(struct mode_tree_list
*mtl
, uint64_t tag
)
124 struct mode_tree_item
*mti
, *child
;
126 TAILQ_FOREACH(mti
, mtl
, entry
) {
129 child
= mode_tree_find_item(&mti
->children
, tag
);
137 mode_tree_free_item(struct mode_tree_item
*mti
)
139 mode_tree_free_items(&mti
->children
);
141 free((void *)mti
->name
);
142 free((void *)mti
->text
);
143 free((void *)mti
->keystr
);
149 mode_tree_free_items(struct mode_tree_list
*mtl
)
151 struct mode_tree_item
*mti
, *mti1
;
153 TAILQ_FOREACH_SAFE(mti
, mtl
, entry
, mti1
) {
154 TAILQ_REMOVE(mtl
, mti
, entry
);
155 mode_tree_free_item(mti
);
160 mode_tree_check_selected(struct mode_tree_data
*mtd
)
163 * If the current line would now be off screen reset the offset to the
166 if (mtd
->current
> mtd
->height
- 1)
167 mtd
->offset
= mtd
->current
- mtd
->height
+ 1;
171 mode_tree_clear_lines(struct mode_tree_data
*mtd
)
173 free(mtd
->line_list
);
174 mtd
->line_list
= NULL
;
179 mode_tree_build_lines(struct mode_tree_data
*mtd
,
180 struct mode_tree_list
*mtl
, u_int depth
)
182 struct mode_tree_item
*mti
;
183 struct mode_tree_line
*line
;
188 TAILQ_FOREACH(mti
, mtl
, entry
) {
189 mtd
->line_list
= xreallocarray(mtd
->line_list
,
190 mtd
->line_size
+ 1, sizeof *mtd
->line_list
);
192 line
= &mtd
->line_list
[mtd
->line_size
++];
195 line
->last
= (mti
== TAILQ_LAST(mtl
, mode_tree_list
));
197 mti
->line
= (mtd
->line_size
- 1);
198 if (!TAILQ_EMPTY(&mti
->children
))
201 mode_tree_build_lines(mtd
, &mti
->children
, depth
+ 1);
203 if (mtd
->keycb
!= NULL
) {
204 mti
->key
= mtd
->keycb(mtd
->modedata
, mti
->itemdata
,
206 if (mti
->key
== KEYC_UNKNOWN
)
207 mti
->key
= KEYC_NONE
;
208 } else if (mti
->line
< 10)
209 mti
->key
= '0' + mti
->line
;
210 else if (mti
->line
< 36)
211 mti
->key
= KEYC_META
|('a' + mti
->line
- 10);
213 mti
->key
= KEYC_NONE
;
214 if (mti
->key
!= KEYC_NONE
) {
215 mti
->keystr
= xstrdup(key_string_lookup_key(mti
->key
,
217 mti
->keylen
= strlen(mti
->keystr
);
223 TAILQ_FOREACH(mti
, mtl
, entry
) {
224 for (i
= 0; i
< mtd
->line_size
; i
++) {
225 line
= &mtd
->line_list
[i
];
226 if (line
->item
== mti
)
233 mode_tree_clear_tagged(struct mode_tree_list
*mtl
)
235 struct mode_tree_item
*mti
;
237 TAILQ_FOREACH(mti
, mtl
, entry
) {
239 mode_tree_clear_tagged(&mti
->children
);
244 mode_tree_up(struct mode_tree_data
*mtd
, int wrap
)
246 if (mtd
->current
== 0) {
248 mtd
->current
= mtd
->line_size
- 1;
249 if (mtd
->line_size
>= mtd
->height
)
250 mtd
->offset
= mtd
->line_size
- mtd
->height
;
254 if (mtd
->current
< mtd
->offset
)
260 mode_tree_down(struct mode_tree_data
*mtd
, int wrap
)
262 if (mtd
->current
== mtd
->line_size
- 1) {
269 if (mtd
->current
> mtd
->offset
+ mtd
->height
- 1)
275 mode_tree_get_current(struct mode_tree_data
*mtd
)
277 return (mtd
->line_list
[mtd
->current
].item
->itemdata
);
281 mode_tree_get_current_name(struct mode_tree_data
*mtd
)
283 return (mtd
->line_list
[mtd
->current
].item
->name
);
287 mode_tree_expand_current(struct mode_tree_data
*mtd
)
289 if (!mtd
->line_list
[mtd
->current
].item
->expanded
) {
290 mtd
->line_list
[mtd
->current
].item
->expanded
= 1;
291 mode_tree_build(mtd
);
296 mode_tree_collapse_current(struct mode_tree_data
*mtd
)
298 if (mtd
->line_list
[mtd
->current
].item
->expanded
) {
299 mtd
->line_list
[mtd
->current
].item
->expanded
= 0;
300 mode_tree_build(mtd
);
305 mode_tree_get_tag(struct mode_tree_data
*mtd
, uint64_t tag
, u_int
*found
)
309 for (i
= 0; i
< mtd
->line_size
; i
++) {
310 if (mtd
->line_list
[i
].item
->tag
== tag
)
313 if (i
!= mtd
->line_size
) {
321 mode_tree_expand(struct mode_tree_data
*mtd
, uint64_t tag
)
325 if (!mode_tree_get_tag(mtd
, tag
, &found
))
327 if (!mtd
->line_list
[found
].item
->expanded
) {
328 mtd
->line_list
[found
].item
->expanded
= 1;
329 mode_tree_build(mtd
);
334 mode_tree_set_current(struct mode_tree_data
*mtd
, uint64_t tag
)
338 if (mode_tree_get_tag(mtd
, tag
, &found
)) {
339 mtd
->current
= found
;
340 if (mtd
->current
> mtd
->height
- 1)
341 mtd
->offset
= mtd
->current
- mtd
->height
+ 1;
352 mode_tree_count_tagged(struct mode_tree_data
*mtd
)
354 struct mode_tree_item
*mti
;
358 for (i
= 0; i
< mtd
->line_size
; i
++) {
359 mti
= mtd
->line_list
[i
].item
;
367 mode_tree_each_tagged(struct mode_tree_data
*mtd
, mode_tree_each_cb cb
,
368 struct client
*c
, key_code key
, int current
)
370 struct mode_tree_item
*mti
;
375 for (i
= 0; i
< mtd
->line_size
; i
++) {
376 mti
= mtd
->line_list
[i
].item
;
379 cb(mtd
->modedata
, mti
->itemdata
, c
, key
);
382 if (!fired
&& current
) {
383 mti
= mtd
->line_list
[mtd
->current
].item
;
384 cb(mtd
->modedata
, mti
->itemdata
, c
, key
);
388 struct mode_tree_data
*
389 mode_tree_start(struct window_pane
*wp
, struct args
*args
,
390 mode_tree_build_cb buildcb
, mode_tree_draw_cb drawcb
,
391 mode_tree_search_cb searchcb
, mode_tree_menu_cb menucb
,
392 mode_tree_height_cb heightcb
, mode_tree_key_cb keycb
, void *modedata
,
393 const struct menu_item
*menu
, const char **sort_list
, u_int sort_size
,
396 struct mode_tree_data
*mtd
;
400 mtd
= xcalloc(1, sizeof *mtd
);
404 mtd
->modedata
= modedata
;
407 mtd
->sort_list
= sort_list
;
408 mtd
->sort_size
= sort_size
;
410 mtd
->preview
= !args_has(args
, 'N');
412 sort
= args_get(args
, 'O');
414 for (i
= 0; i
< sort_size
; i
++) {
415 if (strcasecmp(sort
, sort_list
[i
]) == 0)
416 mtd
->sort_crit
.field
= i
;
419 mtd
->sort_crit
.reversed
= args_has(args
, 'r');
421 if (args_has(args
, 'f'))
422 mtd
->filter
= xstrdup(args_get(args
, 'f'));
426 mtd
->buildcb
= buildcb
;
427 mtd
->drawcb
= drawcb
;
428 mtd
->searchcb
= searchcb
;
429 mtd
->menucb
= menucb
;
430 mtd
->heightcb
= heightcb
;
433 TAILQ_INIT(&mtd
->children
);
436 screen_init(*s
, screen_size_x(&wp
->base
), screen_size_y(&wp
->base
), 0);
437 (*s
)->mode
&= ~MODE_CURSOR
;
443 mode_tree_zoom(struct mode_tree_data
*mtd
, struct args
*args
)
445 struct window_pane
*wp
= mtd
->wp
;
447 if (args_has(args
, 'Z')) {
448 mtd
->zoomed
= (wp
->window
->flags
& WINDOW_ZOOMED
);
449 if (!mtd
->zoomed
&& window_zoom(wp
) == 0)
450 server_redraw_window(wp
->window
);
456 mode_tree_set_height(struct mode_tree_data
*mtd
)
458 struct screen
*s
= &mtd
->screen
;
461 if (mtd
->heightcb
!= NULL
) {
462 height
= mtd
->heightcb(mtd
, screen_size_y(s
));
463 if (height
< screen_size_y(s
))
464 mtd
->height
= screen_size_y(s
) - height
;
466 mtd
->height
= (screen_size_y(s
) / 3) * 2;
467 if (mtd
->height
> mtd
->line_size
)
468 mtd
->height
= screen_size_y(s
) / 2;
470 if (mtd
->height
< 10)
471 mtd
->height
= screen_size_y(s
);
472 if (screen_size_y(s
) - mtd
->height
< 2)
473 mtd
->height
= screen_size_y(s
);
477 mode_tree_build(struct mode_tree_data
*mtd
)
479 struct screen
*s
= &mtd
->screen
;
482 if (mtd
->line_list
!= NULL
)
483 tag
= mtd
->line_list
[mtd
->current
].item
->tag
;
487 TAILQ_CONCAT(&mtd
->saved
, &mtd
->children
, entry
);
488 TAILQ_INIT(&mtd
->children
);
490 mtd
->buildcb(mtd
->modedata
, &mtd
->sort_crit
, &tag
, mtd
->filter
);
491 mtd
->no_matches
= TAILQ_EMPTY(&mtd
->children
);
493 mtd
->buildcb(mtd
->modedata
, &mtd
->sort_crit
, &tag
, NULL
);
495 mode_tree_free_items(&mtd
->saved
);
496 TAILQ_INIT(&mtd
->saved
);
498 mode_tree_clear_lines(mtd
);
499 mode_tree_build_lines(mtd
, &mtd
->children
, 0);
501 if (tag
== UINT64_MAX
)
502 tag
= mtd
->line_list
[mtd
->current
].item
->tag
;
503 mode_tree_set_current(mtd
, tag
);
505 mtd
->width
= screen_size_x(s
);
507 mode_tree_set_height(mtd
);
509 mtd
->height
= screen_size_y(s
);
510 mode_tree_check_selected(mtd
);
514 mode_tree_remove_ref(struct mode_tree_data
*mtd
)
516 if (--mtd
->references
== 0)
521 mode_tree_free(struct mode_tree_data
*mtd
)
523 struct window_pane
*wp
= mtd
->wp
;
525 if (mtd
->zoomed
== 0)
526 server_unzoom_window(wp
->window
);
528 mode_tree_free_items(&mtd
->children
);
529 mode_tree_clear_lines(mtd
);
530 screen_free(&mtd
->screen
);
536 mode_tree_remove_ref(mtd
);
540 mode_tree_resize(struct mode_tree_data
*mtd
, u_int sx
, u_int sy
)
542 struct screen
*s
= &mtd
->screen
;
544 screen_resize(s
, sx
, sy
, 0);
546 mode_tree_build(mtd
);
549 mtd
->wp
->flags
|= PANE_REDRAW
;
552 struct mode_tree_item
*
553 mode_tree_add(struct mode_tree_data
*mtd
, struct mode_tree_item
*parent
,
554 void *itemdata
, uint64_t tag
, const char *name
, const char *text
,
557 struct mode_tree_item
*mti
, *saved
;
559 log_debug("%s: %llu, %s %s", __func__
, (unsigned long long)tag
,
560 name
, (text
== NULL
? "" : text
));
562 mti
= xcalloc(1, sizeof *mti
);
563 mti
->parent
= parent
;
564 mti
->itemdata
= itemdata
;
567 mti
->name
= xstrdup(name
);
569 mti
->text
= xstrdup(text
);
571 saved
= mode_tree_find_item(&mtd
->saved
, tag
);
573 if (parent
== NULL
|| parent
->expanded
)
574 mti
->tagged
= saved
->tagged
;
575 mti
->expanded
= saved
->expanded
;
576 } else if (expanded
== -1)
579 mti
->expanded
= expanded
;
581 TAILQ_INIT(&mti
->children
);
584 TAILQ_INSERT_TAIL(&parent
->children
, mti
, entry
);
586 TAILQ_INSERT_TAIL(&mtd
->children
, mti
, entry
);
592 mode_tree_draw_as_parent(struct mode_tree_item
*mti
)
594 mti
->draw_as_parent
= 1;
598 mode_tree_no_tag(struct mode_tree_item
*mti
)
604 mode_tree_remove(struct mode_tree_data
*mtd
, struct mode_tree_item
*mti
)
606 struct mode_tree_item
*parent
= mti
->parent
;
609 TAILQ_REMOVE(&parent
->children
, mti
, entry
);
611 TAILQ_REMOVE(&mtd
->children
, mti
, entry
);
612 mode_tree_free_item(mti
);
616 mode_tree_draw(struct mode_tree_data
*mtd
)
618 struct window_pane
*wp
= mtd
->wp
;
619 struct screen
*s
= &mtd
->screen
;
620 struct mode_tree_line
*line
;
621 struct mode_tree_item
*mti
;
622 struct options
*oo
= wp
->window
->options
;
623 struct screen_write_ctx ctx
;
624 struct grid_cell gc0
, gc
;
625 u_int w
, h
, i
, j
, sy
, box_x
, box_y
, width
;
626 char *text
, *start
, *key
;
627 const char *tag
, *symbol
;
631 if (mtd
->line_size
== 0)
634 memcpy(&gc0
, &grid_default_cell
, sizeof gc0
);
635 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
636 style_apply(&gc
, oo
, "mode-style", NULL
);
641 screen_write_start(&ctx
, s
);
642 screen_write_clearscreen(&ctx
, 8);
645 for (i
= 0; i
< mtd
->line_size
; i
++) {
646 mti
= mtd
->line_list
[i
].item
;
647 if (mti
->key
== KEYC_NONE
)
649 if ((int)mti
->keylen
+ 3 > keylen
)
650 keylen
= mti
->keylen
+ 3;
653 for (i
= 0; i
< mtd
->line_size
; i
++) {
656 if (i
> mtd
->offset
+ h
- 1)
658 line
= &mtd
->line_list
[i
];
661 screen_write_cursormove(&ctx
, 0, i
- mtd
->offset
, 0);
663 pad
= keylen
- 2 - mti
->keylen
;
664 if (mti
->key
!= KEYC_NONE
)
665 xasprintf(&key
, "(%s)%*s", mti
->keystr
, pad
, "");
671 else if (TAILQ_EMPTY(&mti
->children
))
673 else if (mti
->expanded
)
678 if (line
->depth
== 0)
679 start
= xstrdup(symbol
);
681 size
= (4 * line
->depth
) + 32;
683 start
= xcalloc(1, size
);
684 for (j
= 1; j
< line
->depth
; j
++) {
685 if (mti
->parent
!= NULL
&&
686 mtd
->line_list
[mti
->parent
->line
].last
)
687 strlcat(start
, " ", size
);
689 strlcat(start
, "\001x\001 ", size
);
692 strlcat(start
, "\001mq\001> ", size
);
694 strlcat(start
, "\001tq\001> ", size
);
695 strlcat(start
, symbol
, size
);
702 xasprintf(&text
, "%-*s%s%s%s%s", keylen
, key
, start
, mti
->name
,
703 tag
, (mti
->text
!= NULL
) ? ": " : "" );
704 width
= utf8_cstrwidth(text
);
710 gc
.attr
^= GRID_ATTR_BRIGHT
;
711 gc0
.attr
^= GRID_ATTR_BRIGHT
;
714 if (i
!= mtd
->current
) {
715 screen_write_clearendofline(&ctx
, 8);
716 screen_write_nputs(&ctx
, w
, &gc0
, "%s", text
);
717 if (mti
->text
!= NULL
) {
718 format_draw(&ctx
, &gc0
, w
- width
, mti
->text
,
722 screen_write_clearendofline(&ctx
, gc
.bg
);
723 screen_write_nputs(&ctx
, w
, &gc
, "%s", text
);
724 if (mti
->text
!= NULL
) {
725 format_draw(&ctx
, &gc
, w
- width
, mti
->text
,
733 gc
.attr
^= GRID_ATTR_BRIGHT
;
734 gc0
.attr
^= GRID_ATTR_BRIGHT
;
738 sy
= screen_size_y(s
);
739 if (!mtd
->preview
|| sy
<= 4 || h
<= 4 || sy
- h
<= 4 || w
<= 4)
742 line
= &mtd
->line_list
[mtd
->current
];
744 if (mti
->draw_as_parent
)
747 screen_write_cursormove(&ctx
, 0, h
, 0);
748 screen_write_box(&ctx
, w
, sy
- h
, BOX_LINES_DEFAULT
, NULL
, NULL
);
750 if (mtd
->sort_list
!= NULL
) {
751 xasprintf(&text
, " %s (sort: %s%s)", mti
->name
,
752 mtd
->sort_list
[mtd
->sort_crit
.field
],
753 mtd
->sort_crit
.reversed
? ", reversed" : "");
755 xasprintf(&text
, " %s", mti
->name
);
756 if (w
- 2 >= strlen(text
)) {
757 screen_write_cursormove(&ctx
, 1, h
, 0);
758 screen_write_puts(&ctx
, &gc0
, "%s", text
);
761 n
= (sizeof "no matches") - 1;
763 n
= (sizeof "active") - 1;
764 if (mtd
->filter
!= NULL
&& w
- 2 >= strlen(text
) + 10 + n
+ 2) {
765 screen_write_puts(&ctx
, &gc0
, " (filter: ");
767 screen_write_puts(&ctx
, &gc
, "no matches");
769 screen_write_puts(&ctx
, &gc0
, "active");
770 screen_write_puts(&ctx
, &gc0
, ") ");
772 screen_write_puts(&ctx
, &gc0
, " ");
779 if (box_x
!= 0 && box_y
!= 0) {
780 screen_write_cursormove(&ctx
, 2, h
+ 1, 0);
781 mtd
->drawcb(mtd
->modedata
, mti
->itemdata
, &ctx
, box_x
, box_y
);
785 screen_write_cursormove(&ctx
, 0, mtd
->current
- mtd
->offset
, 0);
786 screen_write_stop(&ctx
);
789 static struct mode_tree_item
*
790 mode_tree_search_for(struct mode_tree_data
*mtd
)
792 struct mode_tree_item
*mti
, *last
, *next
;
794 if (mtd
->search
== NULL
)
797 mti
= last
= mtd
->line_list
[mtd
->current
].item
;
799 if (!TAILQ_EMPTY(&mti
->children
))
800 mti
= TAILQ_FIRST(&mti
->children
);
801 else if ((next
= TAILQ_NEXT(mti
, entry
)) != NULL
)
808 if ((next
= TAILQ_NEXT(mti
, entry
)) != NULL
) {
815 mti
= TAILQ_FIRST(&mtd
->children
);
819 if (mtd
->searchcb
== NULL
) {
820 if (strstr(mti
->name
, mtd
->search
) != NULL
)
824 if (mtd
->searchcb(mtd
->modedata
, mti
->itemdata
, mtd
->search
))
831 mode_tree_search_set(struct mode_tree_data
*mtd
)
833 struct mode_tree_item
*mti
, *loop
;
836 mti
= mode_tree_search_for(mtd
);
842 while (loop
!= NULL
) {
847 mode_tree_build(mtd
);
848 mode_tree_set_current(mtd
, tag
);
850 mtd
->wp
->flags
|= PANE_REDRAW
;
854 mode_tree_search_callback(__unused
struct client
*c
, void *data
, const char *s
,
857 struct mode_tree_data
*mtd
= data
;
863 if (s
== NULL
|| *s
== '\0') {
867 mtd
->search
= xstrdup(s
);
868 mode_tree_search_set(mtd
);
874 mode_tree_search_free(void *data
)
876 mode_tree_remove_ref(data
);
880 mode_tree_filter_callback(__unused
struct client
*c
, void *data
, const char *s
,
883 struct mode_tree_data
*mtd
= data
;
888 if (mtd
->filter
!= NULL
)
890 if (s
== NULL
|| *s
== '\0')
893 mtd
->filter
= xstrdup(s
);
895 mode_tree_build(mtd
);
897 mtd
->wp
->flags
|= PANE_REDRAW
;
903 mode_tree_filter_free(void *data
)
905 mode_tree_remove_ref(data
);
909 mode_tree_menu_callback(__unused
struct menu
*menu
, __unused u_int idx
,
910 key_code key
, void *data
)
912 struct mode_tree_menu
*mtm
= data
;
913 struct mode_tree_data
*mtd
= mtm
->data
;
914 struct mode_tree_item
*mti
;
916 if (mtd
->dead
|| key
== KEYC_NONE
)
919 if (mtm
->line
>= mtd
->line_size
)
921 mti
= mtd
->line_list
[mtm
->line
].item
;
922 if (mti
->itemdata
!= mtm
->itemdata
)
924 mtd
->current
= mtm
->line
;
925 mtd
->menucb(mtd
->modedata
, mtm
->c
, key
);
928 mode_tree_remove_ref(mtd
);
933 mode_tree_display_menu(struct mode_tree_data
*mtd
, struct client
*c
, u_int x
,
934 u_int y
, int outside
)
936 struct mode_tree_item
*mti
;
938 const struct menu_item
*items
;
939 struct mode_tree_menu
*mtm
;
943 if (mtd
->offset
+ y
> mtd
->line_size
- 1)
946 line
= mtd
->offset
+ y
;
947 mti
= mtd
->line_list
[line
].item
;
951 xasprintf(&title
, "#[align=centre]%s", mti
->name
);
953 items
= mode_tree_menu_items
;
956 menu
= menu_create(title
);
957 menu_add_items(menu
, items
, NULL
, NULL
, NULL
);
960 mtm
= xmalloc(sizeof *mtm
);
964 mtm
->itemdata
= mti
->itemdata
;
967 if (x
>= (menu
->width
+ 4) / 2)
968 x
-= (menu
->width
+ 4) / 2;
971 if (menu_display(menu
, 0, NULL
, x
, y
, c
, NULL
, mode_tree_menu_callback
,
977 mode_tree_key(struct mode_tree_data
*mtd
, struct client
*c
, key_code
*key
,
978 struct mouse_event
*m
, u_int
*xp
, u_int
*yp
)
980 struct mode_tree_line
*line
;
981 struct mode_tree_item
*current
, *parent
, *mti
;
985 if (KEYC_IS_MOUSE(*key
) && m
!= NULL
) {
986 if (cmd_mouse_at(mtd
->wp
, m
, &x
, &y
, 0) != 0) {
994 if (x
> mtd
->width
|| y
> mtd
->height
) {
995 if (*key
== KEYC_MOUSEDOWN3_PANE
)
996 mode_tree_display_menu(mtd
, c
, x
, y
, 1);
1001 if (mtd
->offset
+ y
< mtd
->line_size
) {
1002 if (*key
== KEYC_MOUSEDOWN1_PANE
||
1003 *key
== KEYC_MOUSEDOWN3_PANE
||
1004 *key
== KEYC_DOUBLECLICK1_PANE
)
1005 mtd
->current
= mtd
->offset
+ y
;
1006 if (*key
== KEYC_DOUBLECLICK1_PANE
)
1009 if (*key
== KEYC_MOUSEDOWN3_PANE
)
1010 mode_tree_display_menu(mtd
, c
, x
, y
, 0);
1014 if (*key
== KEYC_MOUSEDOWN3_PANE
)
1015 mode_tree_display_menu(mtd
, c
, x
, y
, 0);
1021 line
= &mtd
->line_list
[mtd
->current
];
1022 current
= line
->item
;
1025 for (i
= 0; i
< mtd
->line_size
; i
++) {
1026 if (*key
== mtd
->line_list
[i
].item
->key
) {
1032 if ((u_int
)choice
> mtd
->line_size
- 1) {
1036 mtd
->current
= choice
;
1043 case '\033': /* Escape */
1044 case '\007': /* C-g */
1048 case KEYC_WHEELUP_PANE
:
1049 case '\020': /* C-p */
1050 mode_tree_up(mtd
, 1);
1054 case KEYC_WHEELDOWN_PANE
:
1055 case '\016': /* C-n */
1056 mode_tree_down(mtd
, 1);
1060 case '\002': /* C-b */
1061 for (i
= 0; i
< mtd
->height
; i
++) {
1062 if (mtd
->current
== 0)
1064 mode_tree_up(mtd
, 1);
1069 case '\006': /* C-f */
1070 for (i
= 0; i
< mtd
->height
; i
++) {
1071 if (mtd
->current
== mtd
->line_size
- 1)
1073 mode_tree_down(mtd
, 1);
1081 mtd
->current
= mtd
->line_size
- 1;
1082 if (mtd
->current
> mtd
->height
- 1)
1083 mtd
->offset
= mtd
->current
- mtd
->height
+ 1;
1089 * Do not allow parents and children to both be tagged: untag
1090 * all parents and children of current.
1092 if (current
->no_tag
)
1094 if (!current
->tagged
) {
1095 parent
= current
->parent
;
1096 while (parent
!= NULL
) {
1098 parent
= parent
->parent
;
1100 mode_tree_clear_tagged(¤t
->children
);
1101 current
->tagged
= 1;
1103 current
->tagged
= 0;
1105 mode_tree_down(mtd
, 0);
1108 for (i
= 0; i
< mtd
->line_size
; i
++)
1109 mtd
->line_list
[i
].item
->tagged
= 0;
1111 case '\024': /* C-t */
1112 for (i
= 0; i
< mtd
->line_size
; i
++) {
1113 if ((mtd
->line_list
[i
].item
->parent
== NULL
&&
1114 !mtd
->line_list
[i
].item
->no_tag
) ||
1115 (mtd
->line_list
[i
].item
->parent
!= NULL
&&
1116 mtd
->line_list
[i
].item
->parent
->no_tag
))
1117 mtd
->line_list
[i
].item
->tagged
= 1;
1119 mtd
->line_list
[i
].item
->tagged
= 0;
1123 mtd
->sort_crit
.field
++;
1124 if (mtd
->sort_crit
.field
>= mtd
->sort_size
)
1125 mtd
->sort_crit
.field
= 0;
1126 mode_tree_build(mtd
);
1129 mtd
->sort_crit
.reversed
= !mtd
->sort_crit
.reversed
;
1130 mode_tree_build(mtd
);
1135 if (line
->flat
|| !current
->expanded
)
1136 current
= current
->parent
;
1137 if (current
== NULL
)
1138 mode_tree_up(mtd
, 0);
1140 current
->expanded
= 0;
1141 mtd
->current
= current
->line
;
1142 mode_tree_build(mtd
);
1148 if (line
->flat
|| current
->expanded
)
1149 mode_tree_down(mtd
, 0);
1150 else if (!line
->flat
) {
1151 current
->expanded
= 1;
1152 mode_tree_build(mtd
);
1156 TAILQ_FOREACH(mti
, &mtd
->children
, entry
)
1158 mode_tree_build(mtd
);
1161 TAILQ_FOREACH(mti
, &mtd
->children
, entry
)
1163 mode_tree_build(mtd
);
1167 case '\023': /* C-s */
1169 status_prompt_set(c
, NULL
, "(search) ", "",
1170 mode_tree_search_callback
, mode_tree_search_free
, mtd
,
1171 PROMPT_NOFORMAT
, PROMPT_TYPE_SEARCH
);
1174 mode_tree_search_set(mtd
);
1178 status_prompt_set(c
, NULL
, "(filter) ", mtd
->filter
,
1179 mode_tree_filter_callback
, mode_tree_filter_free
, mtd
,
1180 PROMPT_NOFORMAT
, PROMPT_TYPE_SEARCH
);
1183 mtd
->preview
= !mtd
->preview
;
1184 mode_tree_build(mtd
);
1186 mode_tree_check_selected(mtd
);
1193 mode_tree_run_command(struct client
*c
, struct cmd_find_state
*fs
,
1194 const char *template, const char *name
)
1196 struct cmdq_state
*state
;
1197 char *command
, *error
;
1198 enum cmd_parse_status status
;
1200 command
= cmd_template_replace(template, name
, 1);
1201 if (command
!= NULL
&& *command
!= '\0') {
1202 state
= cmdq_new_state(fs
, NULL
, 0);
1203 status
= cmd_parse_and_append(command
, NULL
, c
, state
, &error
);
1204 if (status
== CMD_PARSE_ERROR
) {
1206 *error
= toupper((u_char
)*error
);
1207 status_message_set(c
, -1, 1, 0, "%s", error
);
1211 cmdq_free_state(state
);