xref: /openbsd-src/usr.bin/tmux/window-tree.c (revision 1a1df6393e0c865b3bd533cc019a70ad3fa9f991)
1 /* $OpenBSD: window-tree.c,v 1.12 2017/07/07 16:27:26 nicm Exp $ */
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 <stdlib.h>
22 #include <string.h>
23 
24 #include "tmux.h"
25 
26 static struct screen	*window_tree_init(struct window_pane *,
27 			     struct cmd_find_state *, struct args *);
28 static void		 window_tree_free(struct window_pane *);
29 static void		 window_tree_resize(struct window_pane *, u_int, u_int);
30 static void		 window_tree_key(struct window_pane *,
31 			     struct client *, struct session *, key_code,
32 			     struct mouse_event *);
33 
34 #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'"
35 
36 const struct window_mode window_tree_mode = {
37 	.name = "tree-mode",
38 
39 	.init = window_tree_init,
40 	.free = window_tree_free,
41 	.resize = window_tree_resize,
42 	.key = window_tree_key,
43 };
44 
45 enum window_tree_sort_type {
46 	WINDOW_TREE_BY_INDEX,
47 	WINDOW_TREE_BY_NAME,
48 	WINDOW_TREE_BY_TIME,
49 };
50 static const char *window_tree_sort_list[] = {
51 	"index",
52 	"name",
53 	"time"
54 };
55 
56 enum window_tree_type {
57 	WINDOW_TREE_NONE,
58 	WINDOW_TREE_SESSION,
59 	WINDOW_TREE_WINDOW,
60 	WINDOW_TREE_PANE,
61 };
62 
63 struct window_tree_itemdata {
64 	enum window_tree_type	type;
65 	int			session;
66 	int			winlink;
67 	int			pane;
68 };
69 
70 struct window_tree_modedata {
71 	struct window_pane		 *wp;
72 	int				  dead;
73 	int				  references;
74 
75 	struct mode_tree_data		 *data;
76 	char				 *command;
77 
78 	struct window_tree_itemdata	**item_list;
79 	u_int				  item_size;
80 
81 	struct client			 *client;
82 	const char			 *entered;
83 
84 	struct cmd_find_state		  fs;
85 	enum window_tree_type		  type;
86 
87 	int				  offset;
88 };
89 
90 static void
91 window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
92     struct winlink **wlp, struct window_pane **wp)
93 {
94 	*wp = NULL;
95 	*wlp = NULL;
96 	*sp = session_find_by_id(item->session);
97 	if (*sp == NULL)
98 		return;
99 	if (item->type == WINDOW_TREE_SESSION) {
100 		*wlp = (*sp)->curw;
101 		*wp = (*wlp)->window->active;
102 		return;
103 	}
104 
105 	*wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
106 	if (*wlp == NULL) {
107 		*sp = NULL;
108 		return;
109 	}
110 	if (item->type == WINDOW_TREE_WINDOW) {
111 		*wp = (*wlp)->window->active;
112 		return;
113 	}
114 
115 	*wp = window_pane_find_by_id(item->pane);
116 	if (!window_has_pane((*wlp)->window, *wp))
117 		*wp = NULL;
118 	if (*wp == NULL) {
119 		*sp = NULL;
120 		*wlp = NULL;
121 		return;
122 	}
123 }
124 
125 static struct window_tree_itemdata *
126 window_tree_add_item(struct window_tree_modedata *data)
127 {
128 	struct window_tree_itemdata	*item;
129 
130 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
131 	    sizeof *data->item_list);
132 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
133 	return (item);
134 }
135 
136 static void
137 window_tree_free_item(struct window_tree_itemdata *item)
138 {
139 	free(item);
140 }
141 
142 static int
143 window_tree_cmp_session_name(const void *a0, const void *b0)
144 {
145 	const struct session *const *a = a0;
146 	const struct session *const *b = b0;
147 
148 	return (strcmp((*a)->name, (*b)->name));
149 }
150 
151 static int
152 window_tree_cmp_session_time(const void *a0, const void *b0)
153 {
154 	const struct session *const *a = a0;
155 	const struct session *const *b = b0;
156 
157 	if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >))
158 		return (-1);
159 	if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <))
160 		return (1);
161 	return (strcmp((*a)->name, (*b)->name));
162 }
163 
164 static int
165 window_tree_cmp_window_name(const void *a0, const void *b0)
166 {
167 	const struct winlink *const *a = a0;
168 	const struct winlink *const *b = b0;
169 
170 	return (strcmp((*a)->window->name, (*b)->window->name));
171 }
172 
173 static int
174 window_tree_cmp_window_time(const void *a0, const void *b0)
175 {
176 	const struct winlink *const *a = a0;
177 	const struct winlink *const *b = b0;
178 
179 	if (timercmp(&(*a)->window->activity_time,
180 	    &(*b)->window->activity_time, >))
181 		return (-1);
182 	if (timercmp(&(*a)->window->activity_time,
183 	    &(*b)->window->activity_time, <))
184 		return (1);
185 	return (strcmp((*a)->window->name, (*b)->window->name));
186 }
187 
188 static int
189 window_tree_cmp_pane_time(const void *a0, const void *b0)
190 {
191 	const struct window_pane *const *a = a0;
192 	const struct window_pane *const *b = b0;
193 
194 	if ((*a)->active_point < (*b)->active_point)
195 		return (-1);
196 	if ((*a)->active_point > (*b)->active_point)
197 		return (1);
198 	return (0);
199 }
200 
201 static void
202 window_tree_build_pane(struct session *s, struct winlink *wl,
203     struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
204 {
205 	struct window_tree_modedata	*data = modedata;
206 	struct window_tree_itemdata	*item;
207 	char				*name, *text;
208 	u_int				 idx;
209 
210 	window_pane_index(wp, &idx);
211 
212 	item = window_tree_add_item(data);
213 	item->type = WINDOW_TREE_PANE;
214 	item->session = s->id;
215 	item->winlink = wl->idx;
216 	item->pane = wp->id;
217 
218 	text = format_single(NULL,
219 	    "#{pane_current_command} \"#{pane_title}\"",
220 	    NULL, s, wl, wp);
221 	xasprintf(&name, "%u", idx);
222 
223 	mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1);
224 	free(text);
225 	free(name);
226 }
227 
228 static int
229 window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
230     u_int sort_type, struct mode_tree_item *parent, const char *filter)
231 {
232 	struct window_tree_modedata	*data = modedata;
233 	struct window_tree_itemdata	*item;
234 	struct mode_tree_item		*mti;
235 	char				*name, *text, *cp;
236 	struct window_pane		*wp, **l;
237 	u_int				 n, i;
238 	int				 expanded;
239 
240 	item = window_tree_add_item(data);
241 	item->type = WINDOW_TREE_WINDOW;
242 	item->session = s->id;
243 	item->winlink = wl->idx;
244 	item->pane = -1;
245 
246 	text = format_single(NULL,
247 	    "#{window_name}#{window_flags} (#{window_panes} panes)",
248 	    NULL, s, wl, NULL);
249 	xasprintf(&name, "%u", wl->idx);
250 
251 	if (data->type == WINDOW_TREE_SESSION ||
252 	    data->type == WINDOW_TREE_WINDOW)
253 		expanded = 0;
254 	else
255 		expanded = 1;
256 	mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text,
257 	    expanded);
258 	free(text);
259 	free(name);
260 
261 	l = NULL;
262 	n = 0;
263 	TAILQ_FOREACH(wp, &wl->window->panes, entry) {
264 		if (filter != NULL) {
265 			cp = format_single(NULL, filter, NULL, s, wl, wp);
266 			if (!format_true(cp)) {
267 				free(cp);
268 				continue;
269 			}
270 			free(cp);
271 		}
272 		l = xreallocarray(l, n + 1, sizeof *l);
273 		l[n++] = wp;
274 	}
275 	if (n == 0) {
276 		window_tree_free_item(item);
277 		data->item_size--;
278 		mode_tree_remove(data->data, mti);
279 		return (0);
280 	}
281 
282 	switch (sort_type) {
283 	case WINDOW_TREE_BY_INDEX:
284 		break;
285 	case WINDOW_TREE_BY_NAME:
286 		/* Panes don't have names, so leave in number order. */
287 		break;
288 	case WINDOW_TREE_BY_TIME:
289 		qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
290 		break;
291 	}
292 
293 	for (i = 0; i < n; i++)
294 		window_tree_build_pane(s, wl, l[i], modedata, mti);
295 	free(l);
296 	return (1);
297 }
298 
299 static void
300 window_tree_build_session(struct session *s, void* modedata,
301     u_int sort_type, const char *filter)
302 {
303 	struct window_tree_modedata	*data = modedata;
304 	struct window_tree_itemdata	*item;
305 	struct mode_tree_item		*mti;
306 	char				*text;
307 	struct winlink			*wl, **l;
308 	u_int				 n, i, empty;
309 	int				 expanded;
310 
311 	item = window_tree_add_item(data);
312 	item->type = WINDOW_TREE_SESSION;
313 	item->session = s->id;
314 	item->winlink = -1;
315 	item->pane = -1;
316 
317 	text = format_single(NULL,
318 	    "#{session_windows} windows"
319 	    "#{?session_grouped, (group ,}"
320 	    "#{session_group}#{?session_grouped,),}"
321 	    "#{?session_attached, (attached),}",
322 	    NULL, s, NULL, NULL);
323 
324 	if (data->type == WINDOW_TREE_SESSION)
325 		expanded = 0;
326 	else
327 		expanded = 1;
328 	mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text,
329 	    expanded);
330 	free(text);
331 
332 	l = NULL;
333 	n = 0;
334 	RB_FOREACH(wl, winlinks, &s->windows) {
335 		l = xreallocarray(l, n + 1, sizeof *l);
336 		l[n++] = wl;
337 	}
338 	switch (sort_type) {
339 	case WINDOW_TREE_BY_INDEX:
340 		break;
341 	case WINDOW_TREE_BY_NAME:
342 		qsort(l, n, sizeof *l, window_tree_cmp_window_name);
343 		break;
344 	case WINDOW_TREE_BY_TIME:
345 		qsort(l, n, sizeof *l, window_tree_cmp_window_time);
346 		break;
347 	}
348 
349 	empty = 0;
350 	for (i = 0; i < n; i++) {
351 		if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
352 		    filter))
353 			empty++;
354 	}
355 	if (empty == n) {
356 		window_tree_free_item(item);
357 		data->item_size--;
358 		mode_tree_remove(data->data, mti);
359 	}
360 	free(l);
361 }
362 
363 static void
364 window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
365     const char *filter)
366 {
367 	struct window_tree_modedata	*data = modedata;
368 	struct session			*s, **l;
369 	u_int				 n, i;
370 
371 	for (i = 0; i < data->item_size; i++)
372 		window_tree_free_item(data->item_list[i]);
373 	free(data->item_list);
374 	data->item_list = NULL;
375 	data->item_size = 0;
376 
377 	l = NULL;
378 	n = 0;
379 	RB_FOREACH(s, sessions, &sessions) {
380 		l = xreallocarray(l, n + 1, sizeof *l);
381 		l[n++] = s;
382 	}
383 	switch (sort_type) {
384 	case WINDOW_TREE_BY_INDEX:
385 		break;
386 	case WINDOW_TREE_BY_NAME:
387 		qsort(l, n, sizeof *l, window_tree_cmp_session_name);
388 		break;
389 	case WINDOW_TREE_BY_TIME:
390 		qsort(l, n, sizeof *l, window_tree_cmp_session_time);
391 		break;
392 	}
393 
394 	for (i = 0; i < n; i++)
395 		window_tree_build_session(l[i], modedata, sort_type, filter);
396 	free(l);
397 
398 	switch (data->type) {
399 	case WINDOW_TREE_NONE:
400 		break;
401 	case WINDOW_TREE_SESSION:
402 		*tag = (uint64_t)data->fs.s;
403 		break;
404 	case WINDOW_TREE_WINDOW:
405 		*tag = (uint64_t)data->fs.wl;
406 		break;
407 	case WINDOW_TREE_PANE:
408 		*tag = (uint64_t)data->fs.wp;
409 		break;
410 	}
411 }
412 
413 static void
414 window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
415     struct screen_write_ctx *ctx, u_int sx, u_int sy)
416 {
417 	struct options		*oo = s->options;
418 	struct winlink		*wl;
419 	struct window		*w;
420 	u_int			 loop, total, visible, each, width, offset;
421 	u_int			 current, start, end, remaining, i;
422 	struct grid_cell	 gc;
423 	int			 colour, active_colour, left, right;
424 	char			*label;
425 	size_t			 len;
426 
427 	total = winlink_count(&s->windows);
428 
429 	memcpy(&gc, &grid_default_cell, sizeof gc);
430 	colour = options_get_number(oo, "display-panes-colour");
431 	active_colour = options_get_number(oo, "display-panes-active-colour");
432 
433 	if (sx / total < 24) {
434 		visible = sx / 24;
435 		if (visible == 0)
436 			visible = 1;
437 	} else
438 		visible = total;
439 
440 	current = 0;
441 	RB_FOREACH(wl, winlinks, &s->windows) {
442 		if (wl == s->curw)
443 			break;
444 		current++;
445 	}
446 
447 	if (current < visible) {
448 		start = 0;
449 		end = visible;
450 	} else if (current >= total - visible) {
451 		start = total - visible;
452 		end = total;
453 	} else {
454 		start = current - (visible / 2);
455 		end = start + visible;
456 	}
457 
458 	if (data->offset < -(int)start)
459 		data->offset = -(int)start;
460 	if (data->offset > (int)(total - end))
461 		data->offset = (int)(total - end);
462 	start += data->offset;
463 	end += data->offset;
464 
465 	left = (start != 0);
466 	right = (end != total);
467 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
468 		left = right = 0;
469 	if (left && right) {
470 		each = (sx - 6) / visible;
471 		remaining = (sx - 6) - (visible * each);
472 	} else if (left || right) {
473 		each = (sx - 3) / visible;
474 		remaining = (sx - 3) - (visible * each);
475 	} else {
476 		each = sx / visible;
477 		remaining = sx - (visible * each);
478 	}
479 	if (each == 0)
480 		return;
481 
482 	if (left) {
483 		screen_write_cursormove(ctx, 2, 0);
484 		screen_write_vline(ctx, sy, 0, 0);
485 		screen_write_cursormove(ctx, 0, sy / 2);
486 		screen_write_puts(ctx, &grid_default_cell, "<");
487 	}
488 	if (right) {
489 		screen_write_cursormove(ctx, sx - 3, 0);
490 		screen_write_vline(ctx, sy, 0, 0);
491 		screen_write_cursormove(ctx, sx - 1, sy / 2);
492 		screen_write_puts(ctx, &grid_default_cell, ">");
493 	}
494 
495 	i = loop = 0;
496 	RB_FOREACH(wl, winlinks, &s->windows) {
497 		if (loop == end)
498 			break;
499 		if (loop < start) {
500 			loop++;
501 			continue;
502 		}
503 		w = wl->window;
504 
505 		if (wl == s->curw)
506 			gc.fg = active_colour;
507 		else
508 			gc.fg = colour;
509 
510 		if (left)
511 			offset = 3 + (i * each);
512 		else
513 			offset = (i * each);
514 		if (loop == end - 1)
515 			width = each + remaining;
516 		else
517 			width = each - 1;
518 
519 		screen_write_cursormove(ctx, offset, 0);
520 		screen_write_preview(ctx, &w->active->base, width, sy);
521 
522 		xasprintf(&label, " %u:%s ", wl->idx, w->name);
523 		if (strlen(label) > width)
524 			xasprintf(&label, " %u ", wl->idx);
525 		len = strlen(label) / 2;
526 		screen_write_cursormove(ctx, offset + (each / 2) - len, sy / 2);
527 		if (len < width)
528 			screen_write_puts(ctx, &gc, "%s", label);
529 		free(label);
530 
531 		if (loop != end - 1) {
532 			screen_write_cursormove(ctx, offset + width, 0);
533 			screen_write_vline(ctx, sy, 0, 0);
534 		}
535 		loop++;
536 
537 		i++;
538 	}
539 }
540 
541 static void
542 window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
543     struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy)
544 {
545 	struct options		*oo = s->options;
546 	struct window_pane	*wp;
547 	u_int			 loop, total, visible, each, width, offset;
548 	u_int			 current, start, end, remaining, i;
549 	struct grid_cell	 gc;
550 	int			 colour, active_colour, left, right;
551 	char			*label;
552 	size_t			 len;
553 
554 	total = window_count_panes(w);
555 
556 	memcpy(&gc, &grid_default_cell, sizeof gc);
557 	colour = options_get_number(oo, "display-panes-colour");
558 	active_colour = options_get_number(oo, "display-panes-active-colour");
559 
560 	if (sx / total < 24) {
561 		visible = sx / 24;
562 		if (visible == 0)
563 			visible = 1;
564 	} else
565 		visible = total;
566 
567 	current = 0;
568 	TAILQ_FOREACH(wp, &w->panes, entry) {
569 		if (wp == w->active)
570 			break;
571 		current++;
572 	}
573 
574 	if (current < visible) {
575 		start = 0;
576 		end = visible;
577 	} else if (current >= total - visible) {
578 		start = total - visible;
579 		end = total;
580 	} else {
581 		start = current - (visible / 2);
582 		end = start + visible;
583 	}
584 
585 	if (data->offset < -(int)start)
586 		data->offset = -(int)start;
587 	if (data->offset > (int)(total - end))
588 		data->offset = (int)(total - end);
589 	start += data->offset;
590 	end += data->offset;
591 
592 	left = (start != 0);
593 	right = (end != total);
594 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
595 		left = right = 0;
596 	if (left && right) {
597 		each = (sx - 6) / visible;
598 		remaining = (sx - 6) - (visible * each);
599 	} else if (left || right) {
600 		each = (sx - 3) / visible;
601 		remaining = (sx - 3) - (visible * each);
602 	} else {
603 		each = sx / visible;
604 		remaining = sx - (visible * each);
605 	}
606 	if (each == 0)
607 		return;
608 
609 	if (left) {
610 		screen_write_cursormove(ctx, 2, 0);
611 		screen_write_vline(ctx, sy, 0, 0);
612 		screen_write_cursormove(ctx, 0, sy / 2);
613 		screen_write_puts(ctx, &grid_default_cell, "<");
614 	}
615 	if (right) {
616 		screen_write_cursormove(ctx, sx - 3, 0);
617 		screen_write_vline(ctx, sy, 0, 0);
618 		screen_write_cursormove(ctx, sx - 1, sy / 2);
619 		screen_write_puts(ctx, &grid_default_cell, ">");
620 	}
621 
622 	i = loop = 0;
623 	TAILQ_FOREACH(wp, &w->panes, entry) {
624 		if (loop == end)
625 			break;
626 		if (loop < start) {
627 			loop++;
628 			continue;
629 		}
630 
631 		if (wp == w->active)
632 			gc.fg = active_colour;
633 		else
634 			gc.fg = colour;
635 
636 		if (left)
637 			offset = 3 + (i * each);
638 		else
639 			offset = (i * each);
640 		if (loop == end - 1)
641 			width = each + remaining;
642 		else
643 			width = each - 1;
644 
645 		screen_write_cursormove(ctx, offset, 0);
646 		screen_write_preview(ctx, &wp->base, width, sy);
647 
648 		xasprintf(&label, " %u ", loop);
649 		len = strlen(label) / 2;
650 		screen_write_cursormove(ctx, offset + (each / 2) - len, sy / 2);
651 		if (len < width)
652 			screen_write_puts(ctx, &gc, "%s", label);
653 		free(label);
654 
655 		if (loop != end - 1) {
656 			screen_write_cursormove(ctx, offset + width, 0);
657 			screen_write_vline(ctx, sy, 0, 0);
658 		}
659 		loop++;
660 
661 		i++;
662 	}
663 }
664 
665 static struct screen *
666 window_tree_draw(void *modedata, void *itemdata, u_int sx, u_int sy)
667 {
668 	struct window_tree_itemdata	*item = itemdata;
669 	struct session			*sp;
670 	struct winlink			*wlp;
671 	struct window_pane		*wp;
672 	static struct screen		 s;
673 	struct screen_write_ctx		 ctx;
674 
675 	window_tree_pull_item(item, &sp, &wlp, &wp);
676 	if (wp == NULL)
677 		return (NULL);
678 
679 	screen_init(&s, sx, sy, 0);
680 	screen_write_start(&ctx, NULL, &s);
681 
682 	switch (item->type) {
683 	case WINDOW_TREE_NONE:
684 		return (0);
685 	case WINDOW_TREE_SESSION:
686 		window_tree_draw_session(modedata, sp, &ctx, sx, sy);
687 		break;
688 	case WINDOW_TREE_WINDOW:
689 		window_tree_draw_window(modedata, sp, wlp->window, &ctx, sx, sy);
690 		break;
691 	case WINDOW_TREE_PANE:
692 		screen_write_preview(&ctx, &wp->base, sx, sy);
693 		break;
694 	}
695 
696 	screen_write_stop(&ctx);
697 	return (&s);
698 }
699 
700 static int
701 window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
702 {
703 	struct window_tree_itemdata	*item = itemdata;
704 	struct session			*s;
705 	struct winlink			*wl;
706 	struct window_pane		*wp;
707 	const char			*cmd;
708 
709 	window_tree_pull_item(item, &s, &wl, &wp);
710 
711 	switch (item->type) {
712 	case WINDOW_TREE_NONE:
713 		return (0);
714 	case WINDOW_TREE_SESSION:
715 		if (s == NULL)
716 			return (0);
717 		return (strstr(s->name, ss) != NULL);
718 	case WINDOW_TREE_WINDOW:
719 		if (s == NULL || wl == NULL)
720 			return (0);
721 		return (strstr(wl->window->name, ss) != NULL);
722 	case WINDOW_TREE_PANE:
723 		if (s == NULL || wl == NULL || wp == NULL)
724 			break;
725 		cmd = get_proc_name(wp->fd, wp->tty);
726 		if (cmd == NULL || *cmd == '\0')
727 			return (0);
728 		return (strstr(cmd, ss) != NULL);
729 	}
730 	return (0);
731 }
732 
733 static struct screen *
734 window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
735     struct args *args)
736 {
737 	struct window_tree_modedata	*data;
738 	struct screen			*s;
739 
740 	wp->modedata = data = xcalloc(1, sizeof *data);
741 
742 	if (args_has(args, 's'))
743 		data->type = WINDOW_TREE_SESSION;
744 	else if (args_has(args, 'w'))
745 		data->type = WINDOW_TREE_WINDOW;
746 	else
747 		data->type = WINDOW_TREE_PANE;
748 	memcpy(&data->fs, fs, sizeof data->fs);
749 
750 	data->wp = wp;
751 	data->references = 1;
752 
753 	if (args == NULL || args->argc == 0)
754 		data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
755 	else
756 		data->command = xstrdup(args->argv[0]);
757 
758 	data->data = mode_tree_start(wp, args, window_tree_build,
759 	    window_tree_draw, window_tree_search, data, window_tree_sort_list,
760 	    nitems(window_tree_sort_list), &s);
761 
762 	mode_tree_build(data->data);
763 	mode_tree_draw(data->data);
764 
765 	data->type = WINDOW_TREE_NONE;
766 
767 	return (s);
768 }
769 
770 static void
771 window_tree_destroy(struct window_tree_modedata *data)
772 {
773 	u_int	i;
774 
775 	if (--data->references != 0)
776 		return;
777 
778 	mode_tree_free(data->data);
779 
780 	for (i = 0; i < data->item_size; i++)
781 		window_tree_free_item(data->item_list[i]);
782 	free(data->item_list);
783 
784 	free(data->command);
785 	free(data);
786 }
787 
788 static void
789 window_tree_free(struct window_pane *wp)
790 {
791 	struct window_tree_modedata *data = wp->modedata;
792 
793 	if (data == NULL)
794 		return;
795 
796 	data->dead = 1;
797 	window_tree_destroy(data);
798 }
799 
800 static void
801 window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
802 {
803 	struct window_tree_modedata	*data = wp->modedata;
804 
805 	mode_tree_resize(data->data, sx, sy);
806 }
807 
808 static char *
809 window_tree_get_target(struct window_tree_itemdata *item,
810     struct cmd_find_state *fs)
811 {
812 	struct session		*s;
813 	struct winlink		*wl;
814 	struct window_pane	*wp;
815 	char			*target;
816 
817 	window_tree_pull_item(item, &s, &wl, &wp);
818 
819 	target = NULL;
820 	switch (item->type) {
821 	case WINDOW_TREE_NONE:
822 		break;
823 	case WINDOW_TREE_SESSION:
824 		if (s == NULL)
825 			break;
826 		xasprintf(&target, "=%s:", s->name);
827 		break;
828 	case WINDOW_TREE_WINDOW:
829 		if (s == NULL || wl == NULL)
830 			break;
831 		xasprintf(&target, "=%s:%u.", s->name, wl->idx);
832 		break;
833 	case WINDOW_TREE_PANE:
834 		if (s == NULL || wl == NULL || wp == NULL)
835 			break;
836 		xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
837 		break;
838 	}
839 	if (target == NULL)
840 		cmd_find_clear_state(fs, 0);
841 	else
842 		cmd_find_from_winlink_pane(fs, wl, wp);
843 	return (target);
844 }
845 
846 static void
847 window_tree_command_each(void* modedata, void* itemdata, __unused key_code key)
848 {
849 	struct window_tree_modedata	*data = modedata;
850 	struct window_tree_itemdata	*item = itemdata;
851 	char				*name;
852 	struct cmd_find_state		 fs;
853 
854 	name = window_tree_get_target(item, &fs);
855 	if (name != NULL)
856 		mode_tree_run_command(data->client, &fs, data->entered, name);
857 	free(name);
858 }
859 
860 static enum cmd_retval
861 window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
862 {
863 	struct window_tree_modedata	*data = modedata;
864 
865 	if (!data->dead) {
866 		mode_tree_build(data->data);
867 		mode_tree_draw(data->data);
868 		data->wp->flags |= PANE_REDRAW;
869 	}
870 	window_tree_destroy(data);
871 	return (CMD_RETURN_NORMAL);
872 }
873 
874 static int
875 window_tree_command_callback(struct client *c, void *modedata, const char *s,
876     __unused int done)
877 {
878 	struct window_tree_modedata	*data = modedata;
879 
880 	if (data->dead)
881 		return (0);
882 
883 	data->client = c;
884 	data->entered = s;
885 
886 	mode_tree_each_tagged(data->data, window_tree_command_each, KEYC_NONE,
887 	    1);
888 
889 	data->client = NULL;
890 	data->entered = NULL;
891 
892 	data->references++;
893 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
894 
895 	return (0);
896 }
897 
898 static void
899 window_tree_command_free(void *modedata)
900 {
901 	struct window_tree_modedata	*data = modedata;
902 
903 	window_tree_destroy(data);
904 }
905 
906 static void
907 window_tree_key(struct window_pane *wp, struct client *c,
908     __unused struct session *s, key_code key, struct mouse_event *m)
909 {
910 	struct window_tree_modedata	*data = wp->modedata;
911 	struct window_tree_itemdata	*item;
912 	char				*command, *name, *prompt;
913 	struct cmd_find_state		 fs;
914 	int				 finished;
915 	u_int				 tagged;
916 
917 	item = mode_tree_get_current(data->data);
918 	finished = mode_tree_key(data->data, c, &key, m);
919 	if (item != mode_tree_get_current(data->data))
920 		data->offset = 0;
921 	switch (key) {
922 	case '<':
923 		data->offset--;
924 		break;
925 	case '>':
926 		data->offset++;
927 		break;
928 	case ':':
929 		tagged = mode_tree_count_tagged(data->data);
930 		if (tagged != 0)
931 			xasprintf(&prompt, "(%u tagged) ", tagged);
932 		else
933 			xasprintf(&prompt, "(current) ");
934 		data->references++;
935 		status_prompt_set(c, prompt, "", window_tree_command_callback,
936 		    window_tree_command_free, data, PROMPT_NOFORMAT);
937 		free(prompt);
938 		break;
939 	case '\r':
940 		item = mode_tree_get_current(data->data);
941 		command = xstrdup(data->command);
942 		name = window_tree_get_target(item, &fs);
943 		window_pane_reset_mode(wp);
944 		if (name != NULL)
945 			mode_tree_run_command(c, NULL, command, name);
946 		free(name);
947 		free(command);
948 		return;
949 	}
950 	if (finished)
951 		window_pane_reset_mode(wp);
952 	else {
953 		mode_tree_draw(data->data);
954 		wp->flags |= PANE_REDRAW;
955 	}
956 }
957