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 enum mode_tree_search_dir
{
29 MODE_TREE_SEARCH_FORWARD
,
30 MODE_TREE_SEARCH_BACKWARD
33 enum mode_tree_preview
{
34 MODE_TREE_PREVIEW_OFF
,
35 MODE_TREE_PREVIEW_NORMAL
,
39 struct mode_tree_item
;
40 TAILQ_HEAD(mode_tree_list
, mode_tree_item
);
42 struct mode_tree_data
{
47 struct window_pane
*wp
;
49 const struct menu_item
*menu
;
51 const char **sort_list
;
53 struct mode_tree_sort_criteria sort_crit
;
55 mode_tree_build_cb buildcb
;
56 mode_tree_draw_cb drawcb
;
57 mode_tree_search_cb searchcb
;
58 mode_tree_menu_cb menucb
;
59 mode_tree_height_cb heightcb
;
60 mode_tree_key_cb keycb
;
62 struct mode_tree_list children
;
63 struct mode_tree_list saved
;
65 struct mode_tree_line
*line_list
;
82 enum mode_tree_search_dir search_dir
;
85 struct mode_tree_item
{
86 struct mode_tree_item
*parent
;
104 struct mode_tree_list children
;
105 TAILQ_ENTRY(mode_tree_item
) entry
;
108 struct mode_tree_line
{
109 struct mode_tree_item
*item
;
115 struct mode_tree_menu
{
116 struct mode_tree_data
*data
;
121 static void mode_tree_free_items(struct mode_tree_list
*);
123 static const struct menu_item mode_tree_menu_items
[] = {
124 { "Scroll Left", '<', NULL
},
125 { "Scroll Right", '>', NULL
},
126 { "", KEYC_NONE
, NULL
},
127 { "Cancel", 'q', NULL
},
129 { NULL
, KEYC_NONE
, NULL
}
132 static struct mode_tree_item
*
133 mode_tree_find_item(struct mode_tree_list
*mtl
, uint64_t tag
)
135 struct mode_tree_item
*mti
, *child
;
137 TAILQ_FOREACH(mti
, mtl
, entry
) {
140 child
= mode_tree_find_item(&mti
->children
, tag
);
148 mode_tree_free_item(struct mode_tree_item
*mti
)
150 mode_tree_free_items(&mti
->children
);
152 free((void *)mti
->name
);
153 free((void *)mti
->text
);
154 free((void *)mti
->keystr
);
160 mode_tree_free_items(struct mode_tree_list
*mtl
)
162 struct mode_tree_item
*mti
, *mti1
;
164 TAILQ_FOREACH_SAFE(mti
, mtl
, entry
, mti1
) {
165 TAILQ_REMOVE(mtl
, mti
, entry
);
166 mode_tree_free_item(mti
);
171 mode_tree_check_selected(struct mode_tree_data
*mtd
)
174 * If the current line would now be off screen reset the offset to the
177 if (mtd
->current
> mtd
->height
- 1)
178 mtd
->offset
= mtd
->current
- mtd
->height
+ 1;
182 mode_tree_clear_lines(struct mode_tree_data
*mtd
)
184 free(mtd
->line_list
);
185 mtd
->line_list
= NULL
;
190 mode_tree_build_lines(struct mode_tree_data
*mtd
,
191 struct mode_tree_list
*mtl
, u_int depth
)
193 struct mode_tree_item
*mti
;
194 struct mode_tree_line
*line
;
199 TAILQ_FOREACH(mti
, mtl
, entry
) {
200 mtd
->line_list
= xreallocarray(mtd
->line_list
,
201 mtd
->line_size
+ 1, sizeof *mtd
->line_list
);
203 line
= &mtd
->line_list
[mtd
->line_size
++];
206 line
->last
= (mti
== TAILQ_LAST(mtl
, mode_tree_list
));
208 mti
->line
= (mtd
->line_size
- 1);
209 if (!TAILQ_EMPTY(&mti
->children
))
212 mode_tree_build_lines(mtd
, &mti
->children
, depth
+ 1);
214 if (mtd
->keycb
!= NULL
) {
215 mti
->key
= mtd
->keycb(mtd
->modedata
, mti
->itemdata
,
217 if (mti
->key
== KEYC_UNKNOWN
)
218 mti
->key
= KEYC_NONE
;
219 } else if (mti
->line
< 10)
220 mti
->key
= '0' + mti
->line
;
221 else if (mti
->line
< 36)
222 mti
->key
= KEYC_META
|('a' + mti
->line
- 10);
224 mti
->key
= KEYC_NONE
;
225 if (mti
->key
!= KEYC_NONE
) {
226 mti
->keystr
= xstrdup(key_string_lookup_key(mti
->key
,
228 mti
->keylen
= strlen(mti
->keystr
);
234 TAILQ_FOREACH(mti
, mtl
, entry
) {
235 for (i
= 0; i
< mtd
->line_size
; i
++) {
236 line
= &mtd
->line_list
[i
];
237 if (line
->item
== mti
)
244 mode_tree_clear_tagged(struct mode_tree_list
*mtl
)
246 struct mode_tree_item
*mti
;
248 TAILQ_FOREACH(mti
, mtl
, entry
) {
250 mode_tree_clear_tagged(&mti
->children
);
255 mode_tree_up(struct mode_tree_data
*mtd
, int wrap
)
257 if (mtd
->current
== 0) {
259 mtd
->current
= mtd
->line_size
- 1;
260 if (mtd
->line_size
>= mtd
->height
)
261 mtd
->offset
= mtd
->line_size
- mtd
->height
;
265 if (mtd
->current
< mtd
->offset
)
271 mode_tree_down(struct mode_tree_data
*mtd
, int wrap
)
273 if (mtd
->current
== mtd
->line_size
- 1) {
281 if (mtd
->current
> mtd
->offset
+ mtd
->height
- 1)
288 mode_tree_get_current(struct mode_tree_data
*mtd
)
290 return (mtd
->line_list
[mtd
->current
].item
->itemdata
);
294 mode_tree_get_current_name(struct mode_tree_data
*mtd
)
296 return (mtd
->line_list
[mtd
->current
].item
->name
);
300 mode_tree_expand_current(struct mode_tree_data
*mtd
)
302 if (!mtd
->line_list
[mtd
->current
].item
->expanded
) {
303 mtd
->line_list
[mtd
->current
].item
->expanded
= 1;
304 mode_tree_build(mtd
);
309 mode_tree_collapse_current(struct mode_tree_data
*mtd
)
311 if (mtd
->line_list
[mtd
->current
].item
->expanded
) {
312 mtd
->line_list
[mtd
->current
].item
->expanded
= 0;
313 mode_tree_build(mtd
);
318 mode_tree_get_tag(struct mode_tree_data
*mtd
, uint64_t tag
, u_int
*found
)
322 for (i
= 0; i
< mtd
->line_size
; i
++) {
323 if (mtd
->line_list
[i
].item
->tag
== tag
)
326 if (i
!= mtd
->line_size
) {
334 mode_tree_expand(struct mode_tree_data
*mtd
, uint64_t tag
)
338 if (!mode_tree_get_tag(mtd
, tag
, &found
))
340 if (!mtd
->line_list
[found
].item
->expanded
) {
341 mtd
->line_list
[found
].item
->expanded
= 1;
342 mode_tree_build(mtd
);
347 mode_tree_set_current(struct mode_tree_data
*mtd
, uint64_t tag
)
351 if (mode_tree_get_tag(mtd
, tag
, &found
)) {
352 mtd
->current
= found
;
353 if (mtd
->current
> mtd
->height
- 1)
354 mtd
->offset
= mtd
->current
- mtd
->height
+ 1;
365 mode_tree_count_tagged(struct mode_tree_data
*mtd
)
367 struct mode_tree_item
*mti
;
371 for (i
= 0; i
< mtd
->line_size
; i
++) {
372 mti
= mtd
->line_list
[i
].item
;
380 mode_tree_each_tagged(struct mode_tree_data
*mtd
, mode_tree_each_cb cb
,
381 struct client
*c
, key_code key
, int current
)
383 struct mode_tree_item
*mti
;
388 for (i
= 0; i
< mtd
->line_size
; i
++) {
389 mti
= mtd
->line_list
[i
].item
;
392 cb(mtd
->modedata
, mti
->itemdata
, c
, key
);
395 if (!fired
&& current
) {
396 mti
= mtd
->line_list
[mtd
->current
].item
;
397 cb(mtd
->modedata
, mti
->itemdata
, c
, key
);
401 struct mode_tree_data
*
402 mode_tree_start(struct window_pane
*wp
, struct args
*args
,
403 mode_tree_build_cb buildcb
, mode_tree_draw_cb drawcb
,
404 mode_tree_search_cb searchcb
, mode_tree_menu_cb menucb
,
405 mode_tree_height_cb heightcb
, mode_tree_key_cb keycb
, void *modedata
,
406 const struct menu_item
*menu
, const char **sort_list
, u_int sort_size
,
409 struct mode_tree_data
*mtd
;
413 mtd
= xcalloc(1, sizeof *mtd
);
417 mtd
->modedata
= modedata
;
420 mtd
->sort_list
= sort_list
;
421 mtd
->sort_size
= sort_size
;
423 if (args_has(args
, 'N') > 1)
424 mtd
->preview
= MODE_TREE_PREVIEW_BIG
;
425 else if (args_has(args
, 'N'))
426 mtd
->preview
= MODE_TREE_PREVIEW_OFF
;
428 mtd
->preview
= MODE_TREE_PREVIEW_NORMAL
;
430 sort
= args_get(args
, 'O');
432 for (i
= 0; i
< sort_size
; i
++) {
433 if (strcasecmp(sort
, sort_list
[i
]) == 0)
434 mtd
->sort_crit
.field
= i
;
437 mtd
->sort_crit
.reversed
= args_has(args
, 'r');
439 if (args_has(args
, 'f'))
440 mtd
->filter
= xstrdup(args_get(args
, 'f'));
444 mtd
->buildcb
= buildcb
;
445 mtd
->drawcb
= drawcb
;
446 mtd
->searchcb
= searchcb
;
447 mtd
->menucb
= menucb
;
448 mtd
->heightcb
= heightcb
;
451 TAILQ_INIT(&mtd
->children
);
454 screen_init(*s
, screen_size_x(&wp
->base
), screen_size_y(&wp
->base
), 0);
455 (*s
)->mode
&= ~MODE_CURSOR
;
461 mode_tree_zoom(struct mode_tree_data
*mtd
, struct args
*args
)
463 struct window_pane
*wp
= mtd
->wp
;
465 if (args_has(args
, 'Z')) {
466 mtd
->zoomed
= (wp
->window
->flags
& WINDOW_ZOOMED
);
467 if (!mtd
->zoomed
&& window_zoom(wp
) == 0)
468 server_redraw_window(wp
->window
);
474 mode_tree_set_height(struct mode_tree_data
*mtd
)
476 struct screen
*s
= &mtd
->screen
;
479 if (mtd
->heightcb
!= NULL
) {
480 height
= mtd
->heightcb(mtd
, screen_size_y(s
));
481 if (height
< screen_size_y(s
))
482 mtd
->height
= screen_size_y(s
) - height
;
484 if (mtd
->preview
== MODE_TREE_PREVIEW_NORMAL
) {
485 mtd
->height
= (screen_size_y(s
) / 3) * 2;
486 if (mtd
->height
> mtd
->line_size
)
487 mtd
->height
= screen_size_y(s
) / 2;
488 if (mtd
->height
< 10)
489 mtd
->height
= screen_size_y(s
);
490 } else if (mtd
->preview
== MODE_TREE_PREVIEW_BIG
) {
491 mtd
->height
= screen_size_y(s
) / 4;
492 if (mtd
->height
> mtd
->line_size
)
493 mtd
->height
= mtd
->line_size
;
497 mtd
->height
= screen_size_y(s
);
499 if (screen_size_y(s
) - mtd
->height
< 2)
500 mtd
->height
= screen_size_y(s
);
504 mode_tree_build(struct mode_tree_data
*mtd
)
506 struct screen
*s
= &mtd
->screen
;
509 if (mtd
->line_list
!= NULL
)
510 tag
= mtd
->line_list
[mtd
->current
].item
->tag
;
514 TAILQ_CONCAT(&mtd
->saved
, &mtd
->children
, entry
);
515 TAILQ_INIT(&mtd
->children
);
517 mtd
->buildcb(mtd
->modedata
, &mtd
->sort_crit
, &tag
, mtd
->filter
);
518 mtd
->no_matches
= TAILQ_EMPTY(&mtd
->children
);
520 mtd
->buildcb(mtd
->modedata
, &mtd
->sort_crit
, &tag
, NULL
);
522 mode_tree_free_items(&mtd
->saved
);
523 TAILQ_INIT(&mtd
->saved
);
525 mode_tree_clear_lines(mtd
);
526 mode_tree_build_lines(mtd
, &mtd
->children
, 0);
528 if (mtd
->line_list
!= NULL
&& tag
== UINT64_MAX
)
529 tag
= mtd
->line_list
[mtd
->current
].item
->tag
;
530 mode_tree_set_current(mtd
, tag
);
532 mtd
->width
= screen_size_x(s
);
533 if (mtd
->preview
!= MODE_TREE_PREVIEW_OFF
)
534 mode_tree_set_height(mtd
);
536 mtd
->height
= screen_size_y(s
);
537 mode_tree_check_selected(mtd
);
541 mode_tree_remove_ref(struct mode_tree_data
*mtd
)
543 if (--mtd
->references
== 0)
548 mode_tree_free(struct mode_tree_data
*mtd
)
550 struct window_pane
*wp
= mtd
->wp
;
552 if (mtd
->zoomed
== 0)
553 server_unzoom_window(wp
->window
);
555 mode_tree_free_items(&mtd
->children
);
556 mode_tree_clear_lines(mtd
);
557 screen_free(&mtd
->screen
);
563 mode_tree_remove_ref(mtd
);
567 mode_tree_resize(struct mode_tree_data
*mtd
, u_int sx
, u_int sy
)
569 struct screen
*s
= &mtd
->screen
;
571 screen_resize(s
, sx
, sy
, 0);
573 mode_tree_build(mtd
);
576 mtd
->wp
->flags
|= PANE_REDRAW
;
579 struct mode_tree_item
*
580 mode_tree_add(struct mode_tree_data
*mtd
, struct mode_tree_item
*parent
,
581 void *itemdata
, uint64_t tag
, const char *name
, const char *text
,
584 struct mode_tree_item
*mti
, *saved
;
586 log_debug("%s: %llu, %s %s", __func__
, (unsigned long long)tag
,
587 name
, (text
== NULL
? "" : text
));
589 mti
= xcalloc(1, sizeof *mti
);
590 mti
->parent
= parent
;
591 mti
->itemdata
= itemdata
;
594 mti
->name
= xstrdup(name
);
596 mti
->text
= xstrdup(text
);
598 saved
= mode_tree_find_item(&mtd
->saved
, tag
);
600 if (parent
== NULL
|| parent
->expanded
)
601 mti
->tagged
= saved
->tagged
;
602 mti
->expanded
= saved
->expanded
;
603 } else if (expanded
== -1)
606 mti
->expanded
= expanded
;
608 TAILQ_INIT(&mti
->children
);
611 TAILQ_INSERT_TAIL(&parent
->children
, mti
, entry
);
613 TAILQ_INSERT_TAIL(&mtd
->children
, mti
, entry
);
619 mode_tree_draw_as_parent(struct mode_tree_item
*mti
)
621 mti
->draw_as_parent
= 1;
625 mode_tree_no_tag(struct mode_tree_item
*mti
)
631 mode_tree_remove(struct mode_tree_data
*mtd
, struct mode_tree_item
*mti
)
633 struct mode_tree_item
*parent
= mti
->parent
;
636 TAILQ_REMOVE(&parent
->children
, mti
, entry
);
638 TAILQ_REMOVE(&mtd
->children
, mti
, entry
);
639 mode_tree_free_item(mti
);
643 mode_tree_draw(struct mode_tree_data
*mtd
)
645 struct window_pane
*wp
= mtd
->wp
;
646 struct screen
*s
= &mtd
->screen
;
647 struct mode_tree_line
*line
;
648 struct mode_tree_item
*mti
;
649 struct options
*oo
= wp
->window
->options
;
650 struct screen_write_ctx ctx
;
651 struct grid_cell gc0
, gc
;
652 u_int w
, h
, i
, j
, sy
, box_x
, box_y
, width
;
653 char *text
, *start
, *key
;
654 const char *tag
, *symbol
;
658 if (mtd
->line_size
== 0)
661 memcpy(&gc0
, &grid_default_cell
, sizeof gc0
);
662 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
663 style_apply(&gc
, oo
, "mode-style", NULL
);
668 screen_write_start(&ctx
, s
);
669 screen_write_clearscreen(&ctx
, 8);
672 for (i
= 0; i
< mtd
->line_size
; i
++) {
673 mti
= mtd
->line_list
[i
].item
;
674 if (mti
->key
== KEYC_NONE
)
676 if ((int)mti
->keylen
+ 3 > keylen
)
677 keylen
= mti
->keylen
+ 3;
680 for (i
= 0; i
< mtd
->line_size
; i
++) {
683 if (i
> mtd
->offset
+ h
- 1)
685 line
= &mtd
->line_list
[i
];
688 screen_write_cursormove(&ctx
, 0, i
- mtd
->offset
, 0);
690 pad
= keylen
- 2 - mti
->keylen
;
691 if (mti
->key
!= KEYC_NONE
)
692 xasprintf(&key
, "(%s)%*s", mti
->keystr
, pad
, "");
698 else if (TAILQ_EMPTY(&mti
->children
))
700 else if (mti
->expanded
)
705 if (line
->depth
== 0)
706 start
= xstrdup(symbol
);
708 size
= (4 * line
->depth
) + 32;
710 start
= xcalloc(1, size
);
711 for (j
= 1; j
< line
->depth
; j
++) {
712 if (mti
->parent
!= NULL
&&
713 mtd
->line_list
[mti
->parent
->line
].last
)
714 strlcat(start
, " ", size
);
716 strlcat(start
, "\001x\001 ", size
);
719 strlcat(start
, "\001mq\001> ", size
);
721 strlcat(start
, "\001tq\001> ", size
);
722 strlcat(start
, symbol
, size
);
729 xasprintf(&text
, "%-*s%s%s%s%s", keylen
, key
, start
, mti
->name
,
730 tag
, (mti
->text
!= NULL
) ? ": " : "" );
731 width
= utf8_cstrwidth(text
);
737 gc
.attr
^= GRID_ATTR_BRIGHT
;
738 gc0
.attr
^= GRID_ATTR_BRIGHT
;
741 if (i
!= mtd
->current
) {
742 screen_write_clearendofline(&ctx
, 8);
743 screen_write_nputs(&ctx
, w
, &gc0
, "%s", text
);
744 if (mti
->text
!= NULL
) {
745 format_draw(&ctx
, &gc0
, w
- width
, mti
->text
,
749 screen_write_clearendofline(&ctx
, gc
.bg
);
750 screen_write_nputs(&ctx
, w
, &gc
, "%s", text
);
751 if (mti
->text
!= NULL
) {
752 format_draw(&ctx
, &gc
, w
- width
, mti
->text
,
760 gc
.attr
^= GRID_ATTR_BRIGHT
;
761 gc0
.attr
^= GRID_ATTR_BRIGHT
;
765 if (mtd
->preview
== MODE_TREE_PREVIEW_OFF
)
768 sy
= screen_size_y(s
);
769 if (sy
<= 4 || h
< 2 || sy
- h
<= 4 || w
<= 4)
772 line
= &mtd
->line_list
[mtd
->current
];
774 if (mti
->draw_as_parent
)
777 screen_write_cursormove(&ctx
, 0, h
, 0);
778 screen_write_box(&ctx
, w
, sy
- h
, BOX_LINES_DEFAULT
, NULL
, NULL
);
780 if (mtd
->sort_list
!= NULL
) {
781 xasprintf(&text
, " %s (sort: %s%s)", mti
->name
,
782 mtd
->sort_list
[mtd
->sort_crit
.field
],
783 mtd
->sort_crit
.reversed
? ", reversed" : "");
785 xasprintf(&text
, " %s", mti
->name
);
786 if (w
- 2 >= strlen(text
)) {
787 screen_write_cursormove(&ctx
, 1, h
, 0);
788 screen_write_puts(&ctx
, &gc0
, "%s", text
);
791 n
= (sizeof "no matches") - 1;
793 n
= (sizeof "active") - 1;
794 if (mtd
->filter
!= NULL
&& w
- 2 >= strlen(text
) + 10 + n
+ 2) {
795 screen_write_puts(&ctx
, &gc0
, " (filter: ");
797 screen_write_puts(&ctx
, &gc
, "no matches");
799 screen_write_puts(&ctx
, &gc0
, "active");
800 screen_write_puts(&ctx
, &gc0
, ") ");
802 screen_write_puts(&ctx
, &gc0
, " ");
809 if (box_x
!= 0 && box_y
!= 0) {
810 screen_write_cursormove(&ctx
, 2, h
+ 1, 0);
811 mtd
->drawcb(mtd
->modedata
, mti
->itemdata
, &ctx
, box_x
, box_y
);
815 screen_write_cursormove(&ctx
, 0, mtd
->current
- mtd
->offset
, 0);
816 screen_write_stop(&ctx
);
819 static struct mode_tree_item
*
820 mode_tree_search_backward(struct mode_tree_data
*mtd
)
822 struct mode_tree_item
*mti
, *last
, *prev
;
824 if (mtd
->search
== NULL
)
827 mti
= last
= mtd
->line_list
[mtd
->current
].item
;
829 if ((prev
= TAILQ_PREV(mti
, mode_tree_list
, entry
)) != NULL
) {
830 /* Point to the last child in the previous subtree. */
831 while (!TAILQ_EMPTY(&prev
->children
))
832 prev
= TAILQ_LAST(&prev
->children
, mode_tree_list
);
835 /* If prev is NULL, jump to the parent. */
840 /* Point to the last child in the last root subtree. */
841 prev
= TAILQ_LAST(&mtd
->children
, mode_tree_list
);
842 while (!TAILQ_EMPTY(&prev
->children
))
843 prev
= TAILQ_LAST(&prev
->children
, mode_tree_list
);
849 if (mtd
->searchcb
== NULL
) {
850 if (strstr(mti
->name
, mtd
->search
) != NULL
)
854 if (mtd
->searchcb(mtd
->modedata
, mti
->itemdata
, mtd
->search
))
861 static struct mode_tree_item
*
862 mode_tree_search_forward(struct mode_tree_data
*mtd
)
864 struct mode_tree_item
*mti
, *last
, *next
;
866 if (mtd
->search
== NULL
)
869 mti
= last
= mtd
->line_list
[mtd
->current
].item
;
871 if (!TAILQ_EMPTY(&mti
->children
))
872 mti
= TAILQ_FIRST(&mti
->children
);
873 else if ((next
= TAILQ_NEXT(mti
, entry
)) != NULL
)
880 if ((next
= TAILQ_NEXT(mti
, entry
)) != NULL
) {
887 mti
= TAILQ_FIRST(&mtd
->children
);
891 if (mtd
->searchcb
== NULL
) {
892 if (strstr(mti
->name
, mtd
->search
) != NULL
)
896 if (mtd
->searchcb(mtd
->modedata
, mti
->itemdata
, mtd
->search
))
903 mode_tree_search_set(struct mode_tree_data
*mtd
)
905 struct mode_tree_item
*mti
, *loop
;
908 if (mtd
->search_dir
== MODE_TREE_SEARCH_FORWARD
)
909 mti
= mode_tree_search_forward(mtd
);
911 mti
= mode_tree_search_backward(mtd
);
917 while (loop
!= NULL
) {
922 mode_tree_build(mtd
);
923 mode_tree_set_current(mtd
, tag
);
925 mtd
->wp
->flags
|= PANE_REDRAW
;
929 mode_tree_search_callback(__unused
struct client
*c
, void *data
, const char *s
,
932 struct mode_tree_data
*mtd
= data
;
938 if (s
== NULL
|| *s
== '\0') {
942 mtd
->search
= xstrdup(s
);
943 mode_tree_search_set(mtd
);
949 mode_tree_search_free(void *data
)
951 mode_tree_remove_ref(data
);
955 mode_tree_filter_callback(__unused
struct client
*c
, void *data
, const char *s
,
958 struct mode_tree_data
*mtd
= data
;
963 if (mtd
->filter
!= NULL
)
965 if (s
== NULL
|| *s
== '\0')
968 mtd
->filter
= xstrdup(s
);
970 mode_tree_build(mtd
);
972 mtd
->wp
->flags
|= PANE_REDRAW
;
978 mode_tree_filter_free(void *data
)
980 mode_tree_remove_ref(data
);
984 mode_tree_menu_callback(__unused
struct menu
*menu
, __unused u_int idx
,
985 key_code key
, void *data
)
987 struct mode_tree_menu
*mtm
= data
;
988 struct mode_tree_data
*mtd
= mtm
->data
;
990 if (mtd
->dead
|| key
== KEYC_NONE
)
993 if (mtm
->line
>= mtd
->line_size
)
995 mtd
->current
= mtm
->line
;
996 mtd
->menucb(mtd
->modedata
, mtm
->c
, key
);
999 mode_tree_remove_ref(mtd
);
1004 mode_tree_display_menu(struct mode_tree_data
*mtd
, struct client
*c
, u_int x
,
1005 u_int y
, int outside
)
1007 struct mode_tree_item
*mti
;
1009 const struct menu_item
*items
;
1010 struct mode_tree_menu
*mtm
;
1014 if (mtd
->offset
+ y
> mtd
->line_size
- 1)
1015 line
= mtd
->current
;
1017 line
= mtd
->offset
+ y
;
1018 mti
= mtd
->line_list
[line
].item
;
1022 xasprintf(&title
, "#[align=centre]%s", mti
->name
);
1024 items
= mode_tree_menu_items
;
1025 title
= xstrdup("");
1027 menu
= menu_create(title
);
1028 menu_add_items(menu
, items
, NULL
, c
, NULL
);
1031 mtm
= xmalloc(sizeof *mtm
);
1037 if (x
>= (menu
->width
+ 4) / 2)
1038 x
-= (menu
->width
+ 4) / 2;
1041 if (menu_display(menu
, 0, 0, NULL
, x
, y
, c
, BOX_LINES_DEFAULT
, NULL
,
1042 NULL
, NULL
, NULL
, mode_tree_menu_callback
, mtm
) != 0)
1047 mode_tree_key(struct mode_tree_data
*mtd
, struct client
*c
, key_code
*key
,
1048 struct mouse_event
*m
, u_int
*xp
, u_int
*yp
)
1050 struct mode_tree_line
*line
;
1051 struct mode_tree_item
*current
, *parent
, *mti
;
1055 if (KEYC_IS_MOUSE(*key
) && m
!= NULL
) {
1056 if (cmd_mouse_at(mtd
->wp
, m
, &x
, &y
, 0) != 0) {
1064 if (x
> mtd
->width
|| y
> mtd
->height
) {
1065 if (*key
== KEYC_MOUSEDOWN3_PANE
)
1066 mode_tree_display_menu(mtd
, c
, x
, y
, 1);
1067 if (mtd
->preview
== MODE_TREE_PREVIEW_OFF
)
1071 if (mtd
->offset
+ y
< mtd
->line_size
) {
1072 if (*key
== KEYC_MOUSEDOWN1_PANE
||
1073 *key
== KEYC_MOUSEDOWN3_PANE
||
1074 *key
== KEYC_DOUBLECLICK1_PANE
)
1075 mtd
->current
= mtd
->offset
+ y
;
1076 if (*key
== KEYC_DOUBLECLICK1_PANE
)
1079 if (*key
== KEYC_MOUSEDOWN3_PANE
)
1080 mode_tree_display_menu(mtd
, c
, x
, y
, 0);
1084 if (*key
== KEYC_MOUSEDOWN3_PANE
)
1085 mode_tree_display_menu(mtd
, c
, x
, y
, 0);
1091 line
= &mtd
->line_list
[mtd
->current
];
1092 current
= line
->item
;
1095 for (i
= 0; i
< mtd
->line_size
; i
++) {
1096 if (*key
== mtd
->line_list
[i
].item
->key
) {
1102 if ((u_int
)choice
> mtd
->line_size
- 1) {
1106 mtd
->current
= choice
;
1113 case '\033': /* Escape */
1118 case KEYC_WHEELUP_PANE
:
1120 mode_tree_up(mtd
, 1);
1124 case KEYC_WHEELDOWN_PANE
:
1126 mode_tree_down(mtd
, 1);
1130 for (i
= 0; i
< mtd
->height
; i
++) {
1131 if (mtd
->current
== 0)
1133 mode_tree_up(mtd
, 1);
1138 for (i
= 0; i
< mtd
->height
; i
++) {
1139 if (mtd
->current
== mtd
->line_size
- 1)
1141 mode_tree_down(mtd
, 1);
1151 mtd
->current
= mtd
->line_size
- 1;
1152 if (mtd
->current
> mtd
->height
- 1)
1153 mtd
->offset
= mtd
->current
- mtd
->height
+ 1;
1159 * Do not allow parents and children to both be tagged: untag
1160 * all parents and children of current.
1162 if (current
->no_tag
)
1164 if (!current
->tagged
) {
1165 parent
= current
->parent
;
1166 while (parent
!= NULL
) {
1168 parent
= parent
->parent
;
1170 mode_tree_clear_tagged(¤t
->children
);
1171 current
->tagged
= 1;
1173 current
->tagged
= 0;
1175 mode_tree_down(mtd
, 0);
1178 for (i
= 0; i
< mtd
->line_size
; i
++)
1179 mtd
->line_list
[i
].item
->tagged
= 0;
1182 for (i
= 0; i
< mtd
->line_size
; i
++) {
1183 if ((mtd
->line_list
[i
].item
->parent
== NULL
&&
1184 !mtd
->line_list
[i
].item
->no_tag
) ||
1185 (mtd
->line_list
[i
].item
->parent
!= NULL
&&
1186 mtd
->line_list
[i
].item
->parent
->no_tag
))
1187 mtd
->line_list
[i
].item
->tagged
= 1;
1189 mtd
->line_list
[i
].item
->tagged
= 0;
1193 mtd
->sort_crit
.field
++;
1194 if (mtd
->sort_crit
.field
>= mtd
->sort_size
)
1195 mtd
->sort_crit
.field
= 0;
1196 mode_tree_build(mtd
);
1199 mtd
->sort_crit
.reversed
= !mtd
->sort_crit
.reversed
;
1200 mode_tree_build(mtd
);
1205 if (line
->flat
|| !current
->expanded
)
1206 current
= current
->parent
;
1207 if (current
== NULL
)
1208 mode_tree_up(mtd
, 0);
1210 current
->expanded
= 0;
1211 mtd
->current
= current
->line
;
1212 mode_tree_build(mtd
);
1218 if (line
->flat
|| current
->expanded
)
1219 mode_tree_down(mtd
, 0);
1220 else if (!line
->flat
) {
1221 current
->expanded
= 1;
1222 mode_tree_build(mtd
);
1226 TAILQ_FOREACH(mti
, &mtd
->children
, entry
)
1228 mode_tree_build(mtd
);
1231 TAILQ_FOREACH(mti
, &mtd
->children
, entry
)
1233 mode_tree_build(mtd
);
1239 status_prompt_set(c
, NULL
, "(search) ", "",
1240 mode_tree_search_callback
, mode_tree_search_free
, mtd
,
1241 PROMPT_NOFORMAT
, PROMPT_TYPE_SEARCH
);
1244 mtd
->search_dir
= MODE_TREE_SEARCH_FORWARD
;
1245 mode_tree_search_set(mtd
);
1248 mtd
->search_dir
= MODE_TREE_SEARCH_BACKWARD
;
1249 mode_tree_search_set(mtd
);
1253 status_prompt_set(c
, NULL
, "(filter) ", mtd
->filter
,
1254 mode_tree_filter_callback
, mode_tree_filter_free
, mtd
,
1255 PROMPT_NOFORMAT
, PROMPT_TYPE_SEARCH
);
1258 switch (mtd
->preview
) {
1259 case MODE_TREE_PREVIEW_OFF
:
1260 mtd
->preview
= MODE_TREE_PREVIEW_BIG
;
1262 case MODE_TREE_PREVIEW_NORMAL
:
1263 mtd
->preview
= MODE_TREE_PREVIEW_OFF
;
1265 case MODE_TREE_PREVIEW_BIG
:
1266 mtd
->preview
= MODE_TREE_PREVIEW_NORMAL
;
1269 mode_tree_build(mtd
);
1270 if (mtd
->preview
!= MODE_TREE_PREVIEW_OFF
)
1271 mode_tree_check_selected(mtd
);
1278 mode_tree_run_command(struct client
*c
, struct cmd_find_state
*fs
,
1279 const char *template, const char *name
)
1281 struct cmdq_state
*state
;
1282 char *command
, *error
;
1283 enum cmd_parse_status status
;
1285 command
= cmd_template_replace(template, name
, 1);
1286 if (command
!= NULL
&& *command
!= '\0') {
1287 state
= cmdq_new_state(fs
, NULL
, 0);
1288 status
= cmd_parse_and_append(command
, NULL
, c
, state
, &error
);
1289 if (status
== CMD_PARSE_ERROR
) {
1291 *error
= toupper((u_char
)*error
);
1292 status_message_set(c
, -1, 1, 0, "%s", error
);
1296 cmdq_free_state(state
);