xref: /openbsd-src/usr.bin/tmux/window-tree.c (revision 42c0683c5609fe346691c97e80c553467d6eec66)
1 /* $OpenBSD: window-tree.c,v 1.13 2017/07/12 14:31:06 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 	if (window_count_panes(wl->window) == 1)
262 		return (1);
263 
264 	l = NULL;
265 	n = 0;
266 
267 	TAILQ_FOREACH(wp, &wl->window->panes, entry) {
268 		if (filter != NULL) {
269 			cp = format_single(NULL, filter, NULL, s, wl, wp);
270 			if (!format_true(cp)) {
271 				free(cp);
272 				continue;
273 			}
274 			free(cp);
275 		}
276 		l = xreallocarray(l, n + 1, sizeof *l);
277 		l[n++] = wp;
278 	}
279 	if (n == 0) {
280 		window_tree_free_item(item);
281 		data->item_size--;
282 		mode_tree_remove(data->data, mti);
283 		return (0);
284 	}
285 
286 	switch (sort_type) {
287 	case WINDOW_TREE_BY_INDEX:
288 		break;
289 	case WINDOW_TREE_BY_NAME:
290 		/* Panes don't have names, so leave in number order. */
291 		break;
292 	case WINDOW_TREE_BY_TIME:
293 		qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
294 		break;
295 	}
296 
297 	for (i = 0; i < n; i++)
298 		window_tree_build_pane(s, wl, l[i], modedata, mti);
299 	free(l);
300 	return (1);
301 }
302 
303 static void
304 window_tree_build_session(struct session *s, void* modedata,
305     u_int sort_type, const char *filter)
306 {
307 	struct window_tree_modedata	*data = modedata;
308 	struct window_tree_itemdata	*item;
309 	struct mode_tree_item		*mti;
310 	char				*text;
311 	struct winlink			*wl, **l;
312 	u_int				 n, i, empty;
313 	int				 expanded;
314 
315 	item = window_tree_add_item(data);
316 	item->type = WINDOW_TREE_SESSION;
317 	item->session = s->id;
318 	item->winlink = -1;
319 	item->pane = -1;
320 
321 	text = format_single(NULL,
322 	    "#{session_windows} windows"
323 	    "#{?session_grouped, (group ,}"
324 	    "#{session_group}#{?session_grouped,),}"
325 	    "#{?session_attached, (attached),}",
326 	    NULL, s, NULL, NULL);
327 
328 	if (data->type == WINDOW_TREE_SESSION)
329 		expanded = 0;
330 	else
331 		expanded = 1;
332 	mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text,
333 	    expanded);
334 	free(text);
335 
336 	l = NULL;
337 	n = 0;
338 	RB_FOREACH(wl, winlinks, &s->windows) {
339 		l = xreallocarray(l, n + 1, sizeof *l);
340 		l[n++] = wl;
341 	}
342 	switch (sort_type) {
343 	case WINDOW_TREE_BY_INDEX:
344 		break;
345 	case WINDOW_TREE_BY_NAME:
346 		qsort(l, n, sizeof *l, window_tree_cmp_window_name);
347 		break;
348 	case WINDOW_TREE_BY_TIME:
349 		qsort(l, n, sizeof *l, window_tree_cmp_window_time);
350 		break;
351 	}
352 
353 	empty = 0;
354 	for (i = 0; i < n; i++) {
355 		if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
356 		    filter))
357 			empty++;
358 	}
359 	if (empty == n) {
360 		window_tree_free_item(item);
361 		data->item_size--;
362 		mode_tree_remove(data->data, mti);
363 	}
364 	free(l);
365 }
366 
367 static void
368 window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
369     const char *filter)
370 {
371 	struct window_tree_modedata	*data = modedata;
372 	struct session			*s, **l;
373 	u_int				 n, i;
374 
375 	for (i = 0; i < data->item_size; i++)
376 		window_tree_free_item(data->item_list[i]);
377 	free(data->item_list);
378 	data->item_list = NULL;
379 	data->item_size = 0;
380 
381 	l = NULL;
382 	n = 0;
383 	RB_FOREACH(s, sessions, &sessions) {
384 		l = xreallocarray(l, n + 1, sizeof *l);
385 		l[n++] = s;
386 	}
387 	switch (sort_type) {
388 	case WINDOW_TREE_BY_INDEX:
389 		break;
390 	case WINDOW_TREE_BY_NAME:
391 		qsort(l, n, sizeof *l, window_tree_cmp_session_name);
392 		break;
393 	case WINDOW_TREE_BY_TIME:
394 		qsort(l, n, sizeof *l, window_tree_cmp_session_time);
395 		break;
396 	}
397 
398 	for (i = 0; i < n; i++)
399 		window_tree_build_session(l[i], modedata, sort_type, filter);
400 	free(l);
401 
402 	switch (data->type) {
403 	case WINDOW_TREE_NONE:
404 		break;
405 	case WINDOW_TREE_SESSION:
406 		*tag = (uint64_t)data->fs.s;
407 		break;
408 	case WINDOW_TREE_WINDOW:
409 		*tag = (uint64_t)data->fs.wl;
410 		break;
411 	case WINDOW_TREE_PANE:
412 		*tag = (uint64_t)data->fs.wp;
413 		break;
414 	}
415 }
416 
417 static void
418 window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
419     struct screen_write_ctx *ctx, u_int sx, u_int sy)
420 {
421 	struct options		*oo = s->options;
422 	struct winlink		*wl;
423 	struct window		*w;
424 	u_int			 loop, total, visible, each, width, offset;
425 	u_int			 current, start, end, remaining, i;
426 	struct grid_cell	 gc;
427 	int			 colour, active_colour, left, right;
428 	char			*label;
429 	size_t			 len;
430 
431 	total = winlink_count(&s->windows);
432 
433 	memcpy(&gc, &grid_default_cell, sizeof gc);
434 	colour = options_get_number(oo, "display-panes-colour");
435 	active_colour = options_get_number(oo, "display-panes-active-colour");
436 
437 	if (sx / total < 24) {
438 		visible = sx / 24;
439 		if (visible == 0)
440 			visible = 1;
441 	} else
442 		visible = total;
443 
444 	current = 0;
445 	RB_FOREACH(wl, winlinks, &s->windows) {
446 		if (wl == s->curw)
447 			break;
448 		current++;
449 	}
450 
451 	if (current < visible) {
452 		start = 0;
453 		end = visible;
454 	} else if (current >= total - visible) {
455 		start = total - visible;
456 		end = total;
457 	} else {
458 		start = current - (visible / 2);
459 		end = start + visible;
460 	}
461 
462 	if (data->offset < -(int)start)
463 		data->offset = -(int)start;
464 	if (data->offset > (int)(total - end))
465 		data->offset = (int)(total - end);
466 	start += data->offset;
467 	end += data->offset;
468 
469 	left = (start != 0);
470 	right = (end != total);
471 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
472 		left = right = 0;
473 	if (left && right) {
474 		each = (sx - 6) / visible;
475 		remaining = (sx - 6) - (visible * each);
476 	} else if (left || right) {
477 		each = (sx - 3) / visible;
478 		remaining = (sx - 3) - (visible * each);
479 	} else {
480 		each = sx / visible;
481 		remaining = sx - (visible * each);
482 	}
483 	if (each == 0)
484 		return;
485 
486 	if (left) {
487 		screen_write_cursormove(ctx, 2, 0);
488 		screen_write_vline(ctx, sy, 0, 0);
489 		screen_write_cursormove(ctx, 0, sy / 2);
490 		screen_write_puts(ctx, &grid_default_cell, "<");
491 	}
492 	if (right) {
493 		screen_write_cursormove(ctx, sx - 3, 0);
494 		screen_write_vline(ctx, sy, 0, 0);
495 		screen_write_cursormove(ctx, sx - 1, sy / 2);
496 		screen_write_puts(ctx, &grid_default_cell, ">");
497 	}
498 
499 	i = loop = 0;
500 	RB_FOREACH(wl, winlinks, &s->windows) {
501 		if (loop == end)
502 			break;
503 		if (loop < start) {
504 			loop++;
505 			continue;
506 		}
507 		w = wl->window;
508 
509 		if (wl == s->curw)
510 			gc.fg = active_colour;
511 		else
512 			gc.fg = colour;
513 
514 		if (left)
515 			offset = 3 + (i * each);
516 		else
517 			offset = (i * each);
518 		if (loop == end - 1)
519 			width = each + remaining;
520 		else
521 			width = each - 1;
522 
523 		screen_write_cursormove(ctx, offset, 0);
524 		screen_write_preview(ctx, &w->active->base, width, sy);
525 
526 		xasprintf(&label, " %u:%s ", wl->idx, w->name);
527 		if (strlen(label) > width)
528 			xasprintf(&label, " %u ", wl->idx);
529 		len = strlen(label) / 2;
530 		screen_write_cursormove(ctx, offset + (each / 2) - len, sy / 2);
531 		if (len < width)
532 			screen_write_puts(ctx, &gc, "%s", label);
533 		free(label);
534 
535 		if (loop != end - 1) {
536 			screen_write_cursormove(ctx, offset + width, 0);
537 			screen_write_vline(ctx, sy, 0, 0);
538 		}
539 		loop++;
540 
541 		i++;
542 	}
543 }
544 
545 static void
546 window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
547     struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy)
548 {
549 	struct options		*oo = s->options;
550 	struct window_pane	*wp;
551 	u_int			 loop, total, visible, each, width, offset;
552 	u_int			 current, start, end, remaining, i;
553 	struct grid_cell	 gc;
554 	int			 colour, active_colour, left, right;
555 	char			*label;
556 	size_t			 len;
557 
558 	total = window_count_panes(w);
559 
560 	memcpy(&gc, &grid_default_cell, sizeof gc);
561 	colour = options_get_number(oo, "display-panes-colour");
562 	active_colour = options_get_number(oo, "display-panes-active-colour");
563 
564 	if (sx / total < 24) {
565 		visible = sx / 24;
566 		if (visible == 0)
567 			visible = 1;
568 	} else
569 		visible = total;
570 
571 	current = 0;
572 	TAILQ_FOREACH(wp, &w->panes, entry) {
573 		if (wp == w->active)
574 			break;
575 		current++;
576 	}
577 
578 	if (current < visible) {
579 		start = 0;
580 		end = visible;
581 	} else if (current >= total - visible) {
582 		start = total - visible;
583 		end = total;
584 	} else {
585 		start = current - (visible / 2);
586 		end = start + visible;
587 	}
588 
589 	if (data->offset < -(int)start)
590 		data->offset = -(int)start;
591 	if (data->offset > (int)(total - end))
592 		data->offset = (int)(total - end);
593 	start += data->offset;
594 	end += data->offset;
595 
596 	left = (start != 0);
597 	right = (end != total);
598 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
599 		left = right = 0;
600 	if (left && right) {
601 		each = (sx - 6) / visible;
602 		remaining = (sx - 6) - (visible * each);
603 	} else if (left || right) {
604 		each = (sx - 3) / visible;
605 		remaining = (sx - 3) - (visible * each);
606 	} else {
607 		each = sx / visible;
608 		remaining = sx - (visible * each);
609 	}
610 	if (each == 0)
611 		return;
612 
613 	if (left) {
614 		screen_write_cursormove(ctx, 2, 0);
615 		screen_write_vline(ctx, sy, 0, 0);
616 		screen_write_cursormove(ctx, 0, sy / 2);
617 		screen_write_puts(ctx, &grid_default_cell, "<");
618 	}
619 	if (right) {
620 		screen_write_cursormove(ctx, sx - 3, 0);
621 		screen_write_vline(ctx, sy, 0, 0);
622 		screen_write_cursormove(ctx, sx - 1, sy / 2);
623 		screen_write_puts(ctx, &grid_default_cell, ">");
624 	}
625 
626 	i = loop = 0;
627 	TAILQ_FOREACH(wp, &w->panes, entry) {
628 		if (loop == end)
629 			break;
630 		if (loop < start) {
631 			loop++;
632 			continue;
633 		}
634 
635 		if (wp == w->active)
636 			gc.fg = active_colour;
637 		else
638 			gc.fg = colour;
639 
640 		if (left)
641 			offset = 3 + (i * each);
642 		else
643 			offset = (i * each);
644 		if (loop == end - 1)
645 			width = each + remaining;
646 		else
647 			width = each - 1;
648 
649 		screen_write_cursormove(ctx, offset, 0);
650 		screen_write_preview(ctx, &wp->base, width, sy);
651 
652 		xasprintf(&label, " %u ", loop);
653 		len = strlen(label) / 2;
654 		screen_write_cursormove(ctx, offset + (each / 2) - len, sy / 2);
655 		if (len < width)
656 			screen_write_puts(ctx, &gc, "%s", label);
657 		free(label);
658 
659 		if (loop != end - 1) {
660 			screen_write_cursormove(ctx, offset + width, 0);
661 			screen_write_vline(ctx, sy, 0, 0);
662 		}
663 		loop++;
664 
665 		i++;
666 	}
667 }
668 
669 static struct screen *
670 window_tree_draw(void *modedata, void *itemdata, u_int sx, u_int sy)
671 {
672 	struct window_tree_itemdata	*item = itemdata;
673 	struct session			*sp;
674 	struct winlink			*wlp;
675 	struct window_pane		*wp;
676 	static struct screen		 s;
677 	struct screen_write_ctx		 ctx;
678 
679 	window_tree_pull_item(item, &sp, &wlp, &wp);
680 	if (wp == NULL)
681 		return (NULL);
682 
683 	screen_init(&s, sx, sy, 0);
684 	screen_write_start(&ctx, NULL, &s);
685 
686 	switch (item->type) {
687 	case WINDOW_TREE_NONE:
688 		return (0);
689 	case WINDOW_TREE_SESSION:
690 		window_tree_draw_session(modedata, sp, &ctx, sx, sy);
691 		break;
692 	case WINDOW_TREE_WINDOW:
693 		window_tree_draw_window(modedata, sp, wlp->window, &ctx, sx, sy);
694 		break;
695 	case WINDOW_TREE_PANE:
696 		screen_write_preview(&ctx, &wp->base, sx, sy);
697 		break;
698 	}
699 
700 	screen_write_stop(&ctx);
701 	return (&s);
702 }
703 
704 static int
705 window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
706 {
707 	struct window_tree_itemdata	*item = itemdata;
708 	struct session			*s;
709 	struct winlink			*wl;
710 	struct window_pane		*wp;
711 	const char			*cmd;
712 
713 	window_tree_pull_item(item, &s, &wl, &wp);
714 
715 	switch (item->type) {
716 	case WINDOW_TREE_NONE:
717 		return (0);
718 	case WINDOW_TREE_SESSION:
719 		if (s == NULL)
720 			return (0);
721 		return (strstr(s->name, ss) != NULL);
722 	case WINDOW_TREE_WINDOW:
723 		if (s == NULL || wl == NULL)
724 			return (0);
725 		return (strstr(wl->window->name, ss) != NULL);
726 	case WINDOW_TREE_PANE:
727 		if (s == NULL || wl == NULL || wp == NULL)
728 			break;
729 		cmd = get_proc_name(wp->fd, wp->tty);
730 		if (cmd == NULL || *cmd == '\0')
731 			return (0);
732 		return (strstr(cmd, ss) != NULL);
733 	}
734 	return (0);
735 }
736 
737 static struct screen *
738 window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
739     struct args *args)
740 {
741 	struct window_tree_modedata	*data;
742 	struct screen			*s;
743 
744 	wp->modedata = data = xcalloc(1, sizeof *data);
745 
746 	if (args_has(args, 's'))
747 		data->type = WINDOW_TREE_SESSION;
748 	else if (args_has(args, 'w'))
749 		data->type = WINDOW_TREE_WINDOW;
750 	else
751 		data->type = WINDOW_TREE_PANE;
752 	memcpy(&data->fs, fs, sizeof data->fs);
753 
754 	data->wp = wp;
755 	data->references = 1;
756 
757 	if (args == NULL || args->argc == 0)
758 		data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
759 	else
760 		data->command = xstrdup(args->argv[0]);
761 
762 	data->data = mode_tree_start(wp, args, window_tree_build,
763 	    window_tree_draw, window_tree_search, data, window_tree_sort_list,
764 	    nitems(window_tree_sort_list), &s);
765 
766 	mode_tree_build(data->data);
767 	mode_tree_draw(data->data);
768 
769 	data->type = WINDOW_TREE_NONE;
770 
771 	return (s);
772 }
773 
774 static void
775 window_tree_destroy(struct window_tree_modedata *data)
776 {
777 	u_int	i;
778 
779 	if (--data->references != 0)
780 		return;
781 
782 	mode_tree_free(data->data);
783 
784 	for (i = 0; i < data->item_size; i++)
785 		window_tree_free_item(data->item_list[i]);
786 	free(data->item_list);
787 
788 	free(data->command);
789 	free(data);
790 }
791 
792 static void
793 window_tree_free(struct window_pane *wp)
794 {
795 	struct window_tree_modedata *data = wp->modedata;
796 
797 	if (data == NULL)
798 		return;
799 
800 	data->dead = 1;
801 	window_tree_destroy(data);
802 }
803 
804 static void
805 window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
806 {
807 	struct window_tree_modedata	*data = wp->modedata;
808 
809 	mode_tree_resize(data->data, sx, sy);
810 }
811 
812 static char *
813 window_tree_get_target(struct window_tree_itemdata *item,
814     struct cmd_find_state *fs)
815 {
816 	struct session		*s;
817 	struct winlink		*wl;
818 	struct window_pane	*wp;
819 	char			*target;
820 
821 	window_tree_pull_item(item, &s, &wl, &wp);
822 
823 	target = NULL;
824 	switch (item->type) {
825 	case WINDOW_TREE_NONE:
826 		break;
827 	case WINDOW_TREE_SESSION:
828 		if (s == NULL)
829 			break;
830 		xasprintf(&target, "=%s:", s->name);
831 		break;
832 	case WINDOW_TREE_WINDOW:
833 		if (s == NULL || wl == NULL)
834 			break;
835 		xasprintf(&target, "=%s:%u.", s->name, wl->idx);
836 		break;
837 	case WINDOW_TREE_PANE:
838 		if (s == NULL || wl == NULL || wp == NULL)
839 			break;
840 		xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
841 		break;
842 	}
843 	if (target == NULL)
844 		cmd_find_clear_state(fs, 0);
845 	else
846 		cmd_find_from_winlink_pane(fs, wl, wp);
847 	return (target);
848 }
849 
850 static void
851 window_tree_command_each(void* modedata, void* itemdata, __unused key_code key)
852 {
853 	struct window_tree_modedata	*data = modedata;
854 	struct window_tree_itemdata	*item = itemdata;
855 	char				*name;
856 	struct cmd_find_state		 fs;
857 
858 	name = window_tree_get_target(item, &fs);
859 	if (name != NULL)
860 		mode_tree_run_command(data->client, &fs, data->entered, name);
861 	free(name);
862 }
863 
864 static enum cmd_retval
865 window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
866 {
867 	struct window_tree_modedata	*data = modedata;
868 
869 	if (!data->dead) {
870 		mode_tree_build(data->data);
871 		mode_tree_draw(data->data);
872 		data->wp->flags |= PANE_REDRAW;
873 	}
874 	window_tree_destroy(data);
875 	return (CMD_RETURN_NORMAL);
876 }
877 
878 static int
879 window_tree_command_callback(struct client *c, void *modedata, const char *s,
880     __unused int done)
881 {
882 	struct window_tree_modedata	*data = modedata;
883 
884 	if (data->dead)
885 		return (0);
886 
887 	data->client = c;
888 	data->entered = s;
889 
890 	mode_tree_each_tagged(data->data, window_tree_command_each, KEYC_NONE,
891 	    1);
892 
893 	data->client = NULL;
894 	data->entered = NULL;
895 
896 	data->references++;
897 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
898 
899 	return (0);
900 }
901 
902 static void
903 window_tree_command_free(void *modedata)
904 {
905 	struct window_tree_modedata	*data = modedata;
906 
907 	window_tree_destroy(data);
908 }
909 
910 static void
911 window_tree_key(struct window_pane *wp, struct client *c,
912     __unused struct session *s, key_code key, struct mouse_event *m)
913 {
914 	struct window_tree_modedata	*data = wp->modedata;
915 	struct window_tree_itemdata	*item;
916 	char				*command, *name, *prompt;
917 	struct cmd_find_state		 fs;
918 	int				 finished;
919 	u_int				 tagged;
920 
921 	item = mode_tree_get_current(data->data);
922 	finished = mode_tree_key(data->data, c, &key, m);
923 	if (item != mode_tree_get_current(data->data))
924 		data->offset = 0;
925 	switch (key) {
926 	case '<':
927 		data->offset--;
928 		break;
929 	case '>':
930 		data->offset++;
931 		break;
932 	case ':':
933 		tagged = mode_tree_count_tagged(data->data);
934 		if (tagged != 0)
935 			xasprintf(&prompt, "(%u tagged) ", tagged);
936 		else
937 			xasprintf(&prompt, "(current) ");
938 		data->references++;
939 		status_prompt_set(c, prompt, "", window_tree_command_callback,
940 		    window_tree_command_free, data, PROMPT_NOFORMAT);
941 		free(prompt);
942 		break;
943 	case '\r':
944 		item = mode_tree_get_current(data->data);
945 		command = xstrdup(data->command);
946 		name = window_tree_get_target(item, &fs);
947 		window_pane_reset_mode(wp);
948 		if (name != NULL)
949 			mode_tree_run_command(c, NULL, command, name);
950 		free(name);
951 		free(command);
952 		return;
953 	}
954 	if (finished)
955 		window_pane_reset_mode(wp);
956 	else {
957 		mode_tree_draw(data->data);
958 		wp->flags |= PANE_REDRAW;
959 	}
960 }
961