xref: /netbsd-src/external/bsd/tmux/dist/window-tree.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "tmux.h"
26 
27 static struct screen	*window_tree_init(struct window_mode_entry *,
28 			     struct cmd_find_state *, struct args *);
29 static void		 window_tree_free(struct window_mode_entry *);
30 static void		 window_tree_resize(struct window_mode_entry *, u_int,
31 			     u_int);
32 static void		 window_tree_key(struct window_mode_entry *,
33 			     struct client *, struct session *,
34 			     struct winlink *, key_code, struct mouse_event *);
35 
36 #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'"
37 
38 #define WINDOW_TREE_DEFAULT_FORMAT \
39 	"#{?pane_format," \
40 		"#{pane_current_command} \"#{pane_title}\"" \
41 	"," \
42 		"#{?window_format," \
43 			"#{window_name}#{window_flags} " \
44 			"(#{window_panes} panes)" \
45 			"#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \
46 		"," \
47 			"#{session_windows} windows" \
48 			"#{?session_grouped, " \
49 				"(group #{session_group}: " \
50 				"#{session_group_list})," \
51 			"}" \
52 			"#{?session_attached, (attached),}" \
53 		"}" \
54 	"}"
55 
56 static const struct menu_item window_tree_menu_items[] = {
57 	{ "Select", 'E', NULL },
58 	{ "Expand", 'R', NULL },
59 	{ "", KEYC_NONE, NULL },
60 	{ "Tag", 't', NULL },
61 	{ "Tag All", '\024', NULL },
62 	{ "Tag None", 'T', NULL },
63 	{ "", KEYC_NONE, NULL },
64 	{ "Kill", 'x', NULL },
65 	{ "Kill Tagged", 'X', NULL },
66 	{ "", KEYC_NONE, NULL },
67 	{ "Cancel", 'q', NULL },
68 
69 	{ NULL, KEYC_NONE, NULL }
70 };
71 
72 const struct window_mode window_tree_mode = {
73 	.name = "tree-mode",
74 	.default_format = WINDOW_TREE_DEFAULT_FORMAT,
75 
76 	.init = window_tree_init,
77 	.free = window_tree_free,
78 	.resize = window_tree_resize,
79 	.key = window_tree_key,
80 };
81 
82 enum window_tree_sort_type {
83 	WINDOW_TREE_BY_INDEX,
84 	WINDOW_TREE_BY_NAME,
85 	WINDOW_TREE_BY_TIME,
86 };
87 static const char *window_tree_sort_list[] = {
88 	"index",
89 	"name",
90 	"time"
91 };
92 
93 enum window_tree_type {
94 	WINDOW_TREE_NONE,
95 	WINDOW_TREE_SESSION,
96 	WINDOW_TREE_WINDOW,
97 	WINDOW_TREE_PANE,
98 };
99 
100 struct window_tree_itemdata {
101 	enum window_tree_type	type;
102 	int			session;
103 	int			winlink;
104 	int			pane;
105 };
106 
107 struct window_tree_modedata {
108 	struct window_pane		 *wp;
109 	int				  dead;
110 	int				  references;
111 
112 	struct mode_tree_data		 *data;
113 	char				 *format;
114 	char				 *command;
115 	int				  squash_groups;
116 
117 	struct window_tree_itemdata	**item_list;
118 	u_int				  item_size;
119 
120 	const char			 *entered;
121 
122 	struct cmd_find_state		  fs;
123 	enum window_tree_type		  type;
124 
125 	int				  offset;
126 
127 	int				  left;
128 	int				  right;
129 	u_int				  start;
130 	u_int				  end;
131 	u_int				  each;
132 };
133 
134 static void
135 window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
136     struct winlink **wlp, struct window_pane **wp)
137 {
138 	*wp = NULL;
139 	*wlp = NULL;
140 	*sp = session_find_by_id(item->session);
141 	if (*sp == NULL)
142 		return;
143 	if (item->type == WINDOW_TREE_SESSION) {
144 		*wlp = (*sp)->curw;
145 		*wp = (*wlp)->window->active;
146 		return;
147 	}
148 
149 	*wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
150 	if (*wlp == NULL) {
151 		*sp = NULL;
152 		return;
153 	}
154 	if (item->type == WINDOW_TREE_WINDOW) {
155 		*wp = (*wlp)->window->active;
156 		return;
157 	}
158 
159 	*wp = window_pane_find_by_id(item->pane);
160 	if (!window_has_pane((*wlp)->window, *wp))
161 		*wp = NULL;
162 	if (*wp == NULL) {
163 		*sp = NULL;
164 		*wlp = NULL;
165 		return;
166 	}
167 }
168 
169 static struct window_tree_itemdata *
170 window_tree_add_item(struct window_tree_modedata *data)
171 {
172 	struct window_tree_itemdata	*item;
173 
174 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
175 	    sizeof *data->item_list);
176 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
177 	return (item);
178 }
179 
180 static void
181 window_tree_free_item(struct window_tree_itemdata *item)
182 {
183 	free(item);
184 }
185 
186 static int
187 window_tree_cmp_session_name(const void *a0, const void *b0)
188 {
189 	const struct session *const *a = a0;
190 	const struct session *const *b = b0;
191 
192 	return (strcmp((*a)->name, (*b)->name));
193 }
194 
195 static int
196 window_tree_cmp_session_time(const void *a0, const void *b0)
197 {
198 	const struct session *const *a = a0;
199 	const struct session *const *b = b0;
200 
201 	if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >))
202 		return (-1);
203 	if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <))
204 		return (1);
205 	return (strcmp((*a)->name, (*b)->name));
206 }
207 
208 static int
209 window_tree_cmp_window_name(const void *a0, const void *b0)
210 {
211 	const struct winlink *const *a = a0;
212 	const struct winlink *const *b = b0;
213 
214 	return (strcmp((*a)->window->name, (*b)->window->name));
215 }
216 
217 static int
218 window_tree_cmp_window_time(const void *a0, const void *b0)
219 {
220 	const struct winlink *const *a = a0;
221 	const struct winlink *const *b = b0;
222 
223 	if (timercmp(&(*a)->window->activity_time,
224 	    &(*b)->window->activity_time, >))
225 		return (-1);
226 	if (timercmp(&(*a)->window->activity_time,
227 	    &(*b)->window->activity_time, <))
228 		return (1);
229 	return (strcmp((*a)->window->name, (*b)->window->name));
230 }
231 
232 static int
233 window_tree_cmp_pane_time(const void *a0, const void *b0)
234 {
235 	const struct window_pane *const *a = a0;
236 	const struct window_pane *const *b = b0;
237 
238 	if ((*a)->active_point < (*b)->active_point)
239 		return (-1);
240 	if ((*a)->active_point > (*b)->active_point)
241 		return (1);
242 	return (0);
243 }
244 
245 static void
246 window_tree_build_pane(struct session *s, struct winlink *wl,
247     struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
248 {
249 	struct window_tree_modedata	*data = modedata;
250 	struct window_tree_itemdata	*item;
251 	char				*name, *text;
252 	u_int				 idx;
253 
254 	window_pane_index(wp, &idx);
255 
256 	item = window_tree_add_item(data);
257 	item->type = WINDOW_TREE_PANE;
258 	item->session = s->id;
259 	item->winlink = wl->idx;
260 	item->pane = wp->id;
261 
262 	text = format_single(NULL, data->format, NULL, s, wl, wp);
263 	xasprintf(&name, "%u", idx);
264 
265 	mode_tree_add(data->data, parent, item, (uintptr_t)wp, name, text, -1);
266 	free(text);
267 	free(name);
268 }
269 
270 static int
271 window_tree_filter_pane(struct session *s, struct winlink *wl,
272     struct window_pane *wp, const char *filter)
273 {
274 	char	*cp;
275 	int	 result;
276 
277 	if (filter == NULL)
278 		return (1);
279 
280 	cp = format_single(NULL, filter, NULL, s, wl, wp);
281 	result = format_true(cp);
282 	free(cp);
283 
284 	return (result);
285 }
286 
287 static int
288 window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
289     u_int sort_type, struct mode_tree_item *parent, const char *filter)
290 {
291 	struct window_tree_modedata	*data = modedata;
292 	struct window_tree_itemdata	*item;
293 	struct mode_tree_item		*mti;
294 	char				*name, *text;
295 	struct window_pane		*wp, **l;
296 	u_int				 n, i;
297 	int				 expanded;
298 
299 	item = window_tree_add_item(data);
300 	item->type = WINDOW_TREE_WINDOW;
301 	item->session = s->id;
302 	item->winlink = wl->idx;
303 	item->pane = -1;
304 
305 	text = format_single(NULL, data->format, NULL, s, wl, NULL);
306 	xasprintf(&name, "%u", wl->idx);
307 
308 	if (data->type == WINDOW_TREE_SESSION ||
309 	    data->type == WINDOW_TREE_WINDOW)
310 		expanded = 0;
311 	else
312 		expanded = 1;
313 	mti = mode_tree_add(data->data, parent, item, (uintptr_t)wl, name, text,
314 	    expanded);
315 	free(text);
316 	free(name);
317 
318 	if ((wp = TAILQ_FIRST(&wl->window->panes)) == NULL)
319 		goto empty;
320 	if (TAILQ_NEXT(wp, entry) == NULL) {
321 		if (!window_tree_filter_pane(s, wl, wp, filter))
322 			goto empty;
323 		return (1);
324 	}
325 
326 	l = NULL;
327 	n = 0;
328 
329 	TAILQ_FOREACH(wp, &wl->window->panes, entry) {
330 		if (!window_tree_filter_pane(s, wl, wp, filter))
331 			continue;
332 		l = xreallocarray(l, n + 1, sizeof *l);
333 		l[n++] = wp;
334 	}
335 	if (n == 0)
336 		goto empty;
337 
338 	switch (sort_type) {
339 	case WINDOW_TREE_BY_INDEX:
340 		break;
341 	case WINDOW_TREE_BY_NAME:
342 		/* Panes don't have names, so leave in number order. */
343 		break;
344 	case WINDOW_TREE_BY_TIME:
345 		qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
346 		break;
347 	}
348 
349 	for (i = 0; i < n; i++)
350 		window_tree_build_pane(s, wl, l[i], modedata, mti);
351 	free(l);
352 	return (1);
353 
354 empty:
355 	window_tree_free_item(item);
356 	data->item_size--;
357 	mode_tree_remove(data->data, mti);
358 	return (0);
359 }
360 
361 static void
362 window_tree_build_session(struct session *s, void* modedata,
363     u_int sort_type, const char *filter)
364 {
365 	struct window_tree_modedata	*data = modedata;
366 	struct window_tree_itemdata	*item;
367 	struct mode_tree_item		*mti;
368 	char				*text;
369 	struct winlink			*wl, **l;
370 	u_int				 n, i, empty;
371 	int				 expanded;
372 
373 	item = window_tree_add_item(data);
374 	item->type = WINDOW_TREE_SESSION;
375 	item->session = s->id;
376 	item->winlink = -1;
377 	item->pane = -1;
378 
379 	text = format_single(NULL, data->format, NULL, s, NULL, NULL);
380 
381 	if (data->type == WINDOW_TREE_SESSION)
382 		expanded = 0;
383 	else
384 		expanded = 1;
385 	mti = mode_tree_add(data->data, NULL, item, (uintptr_t)s, s->name, text,
386 	    expanded);
387 	free(text);
388 
389 	l = NULL;
390 	n = 0;
391 	RB_FOREACH(wl, winlinks, &s->windows) {
392 		l = xreallocarray(l, n + 1, sizeof *l);
393 		l[n++] = wl;
394 	}
395 	switch (sort_type) {
396 	case WINDOW_TREE_BY_INDEX:
397 		break;
398 	case WINDOW_TREE_BY_NAME:
399 		qsort(l, n, sizeof *l, window_tree_cmp_window_name);
400 		break;
401 	case WINDOW_TREE_BY_TIME:
402 		qsort(l, n, sizeof *l, window_tree_cmp_window_time);
403 		break;
404 	}
405 
406 	empty = 0;
407 	for (i = 0; i < n; i++) {
408 		if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
409 		    filter))
410 			empty++;
411 	}
412 	if (empty == n) {
413 		window_tree_free_item(item);
414 		data->item_size--;
415 		mode_tree_remove(data->data, mti);
416 	}
417 	free(l);
418 }
419 
420 static void
421 window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
422     const char *filter)
423 {
424 	struct window_tree_modedata	*data = modedata;
425 	struct session			*s, **l;
426 	struct session_group		*sg, *current;
427 	u_int				 n, i;
428 
429 	current = session_group_contains(data->fs.s);
430 
431 	for (i = 0; i < data->item_size; i++)
432 		window_tree_free_item(data->item_list[i]);
433 	free(data->item_list);
434 	data->item_list = NULL;
435 	data->item_size = 0;
436 
437 	l = NULL;
438 	n = 0;
439 	RB_FOREACH(s, sessions, &sessions) {
440 		if (data->squash_groups &&
441 		    (sg = session_group_contains(s)) != NULL) {
442 			if ((sg == current && s != data->fs.s) ||
443 			    (sg != current && s != TAILQ_FIRST(&sg->sessions)))
444 				continue;
445 		}
446 		l = xreallocarray(l, n + 1, sizeof *l);
447 		l[n++] = s;
448 	}
449 	switch (sort_type) {
450 	case WINDOW_TREE_BY_INDEX:
451 		break;
452 	case WINDOW_TREE_BY_NAME:
453 		qsort(l, n, sizeof *l, window_tree_cmp_session_name);
454 		break;
455 	case WINDOW_TREE_BY_TIME:
456 		qsort(l, n, sizeof *l, window_tree_cmp_session_time);
457 		break;
458 	}
459 
460 	for (i = 0; i < n; i++)
461 		window_tree_build_session(l[i], modedata, sort_type, filter);
462 	free(l);
463 
464 	switch (data->type) {
465 	case WINDOW_TREE_NONE:
466 		break;
467 	case WINDOW_TREE_SESSION:
468 		*tag = (uintptr_t)data->fs.s;
469 		break;
470 	case WINDOW_TREE_WINDOW:
471 		*tag = (uintptr_t)data->fs.wl;
472 		break;
473 	case WINDOW_TREE_PANE:
474 		if (window_count_panes(data->fs.wl->window) == 1)
475 			*tag = (uintptr_t)data->fs.wl;
476 		else
477 			*tag = (uintptr_t)data->fs.wp;
478 		break;
479 	}
480 }
481 
482 static void
483 window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
484     u_int sx, u_int sy, const struct grid_cell *gc, const char *label)
485 {
486 	size_t	 len;
487 	u_int	 ox, oy;
488 
489 	len = strlen(label);
490 	if (sx == 0 || sy == 1 || len > sx)
491 		return;
492 	ox = (sx - len + 1) / 2;
493 	oy = (sy + 1) / 2;
494 
495 	if (ox > 1 && ox + len < sx - 1 && sy >= 3) {
496 		screen_write_cursormove(ctx, px + ox - 1, py + oy - 1, 0);
497 		screen_write_box(ctx, len + 2, 3);
498 	}
499 	screen_write_cursormove(ctx, px + ox, py + oy, 0);
500 	screen_write_puts(ctx, gc, "%s", label);
501 }
502 
503 static void
504 window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
505     struct screen_write_ctx *ctx, u_int sx, u_int sy)
506 {
507 	struct options		*oo = s->options;
508 	struct winlink		*wl;
509 	struct window		*w;
510 	u_int			 cx = ctx->s->cx, cy = ctx->s->cy;
511 	u_int			 loop, total, visible, each, width, offset;
512 	u_int			 current, start, end, remaining, i;
513 	struct grid_cell	 gc;
514 	int			 colour, active_colour, left, right;
515 	char			*label;
516 
517 	total = winlink_count(&s->windows);
518 
519 	memcpy(&gc, &grid_default_cell, sizeof gc);
520 	colour = options_get_number(oo, "display-panes-colour");
521 	active_colour = options_get_number(oo, "display-panes-active-colour");
522 
523 	if (sx / total < 24) {
524 		visible = sx / 24;
525 		if (visible == 0)
526 			visible = 1;
527 	} else
528 		visible = total;
529 
530 	current = 0;
531 	RB_FOREACH(wl, winlinks, &s->windows) {
532 		if (wl == s->curw)
533 			break;
534 		current++;
535 	}
536 
537 	if (current < visible) {
538 		start = 0;
539 		end = visible;
540 	} else if (current >= total - visible) {
541 		start = total - visible;
542 		end = total;
543 	} else {
544 		start = current - (visible / 2);
545 		end = start + visible;
546 	}
547 
548 	if (data->offset < -(int)start)
549 		data->offset = -(int)start;
550 	if (data->offset > (int)(total - end))
551 		data->offset = (int)(total - end);
552 	start += data->offset;
553 	end += data->offset;
554 
555 	left = (start != 0);
556 	right = (end != total);
557 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
558 		left = right = 0;
559 	if (left && right) {
560 		each = (sx - 6) / visible;
561 		remaining = (sx - 6) - (visible * each);
562 	} else if (left || right) {
563 		each = (sx - 3) / visible;
564 		remaining = (sx - 3) - (visible * each);
565 	} else {
566 		each = sx / visible;
567 		remaining = sx - (visible * each);
568 	}
569 	if (each == 0)
570 		return;
571 
572 	if (left) {
573 		data->left = cx + 2;
574 		screen_write_cursormove(ctx, cx + 2, cy, 0);
575 		screen_write_vline(ctx, sy, 0, 0);
576 		screen_write_cursormove(ctx, cx, cy + sy / 2, 0);
577 		screen_write_puts(ctx, &grid_default_cell, "<");
578 	} else
579 		data->left = -1;
580 	if (right) {
581 		data->right = cx + sx - 3;
582 		screen_write_cursormove(ctx, cx + sx - 3, cy, 0);
583 		screen_write_vline(ctx, sy, 0, 0);
584 		screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0);
585 		screen_write_puts(ctx, &grid_default_cell, ">");
586 	} else
587 		data->right = -1;
588 
589 	data->start = start;
590 	data->end = end;
591 	data->each = each;
592 
593 	i = loop = 0;
594 	RB_FOREACH(wl, winlinks, &s->windows) {
595 		if (loop == end)
596 			break;
597 		if (loop < start) {
598 			loop++;
599 			continue;
600 		}
601 		w = wl->window;
602 
603 		if (wl == s->curw)
604 			gc.fg = active_colour;
605 		else
606 			gc.fg = colour;
607 
608 		if (left)
609 			offset = 3 + (i * each);
610 		else
611 			offset = (i * each);
612 		if (loop == end - 1)
613 			width = each + remaining;
614 		else
615 			width = each - 1;
616 
617 		screen_write_cursormove(ctx, cx + offset, cy, 0);
618 		screen_write_preview(ctx, &w->active->base, width, sy);
619 
620 		xasprintf(&label, " %u:%s ", wl->idx, w->name);
621 		if (strlen(label) > width)
622 			xasprintf(&label, " %u ", wl->idx);
623 		window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc,
624 		    label);
625 		free(label);
626 
627 		if (loop != end - 1) {
628 			screen_write_cursormove(ctx, cx + offset + width, cy, 0);
629 			screen_write_vline(ctx, sy, 0, 0);
630 		}
631 		loop++;
632 
633 		i++;
634 	}
635 }
636 
637 static void
638 window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
639     struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy)
640 {
641 	struct options		*oo = s->options;
642 	struct window_pane	*wp;
643 	u_int			 cx = ctx->s->cx, cy = ctx->s->cy;
644 	u_int			 loop, total, visible, each, width, offset;
645 	u_int			 current, start, end, remaining, i;
646 	u_int			 pane_idx;
647 	struct grid_cell	 gc;
648 	int			 colour, active_colour, left, right;
649 	char			*label;
650 
651 	total = window_count_panes(w);
652 
653 	memcpy(&gc, &grid_default_cell, sizeof gc);
654 	colour = options_get_number(oo, "display-panes-colour");
655 	active_colour = options_get_number(oo, "display-panes-active-colour");
656 
657 	if (sx / total < 24) {
658 		visible = sx / 24;
659 		if (visible == 0)
660 			visible = 1;
661 	} else
662 		visible = total;
663 
664 	current = 0;
665 	TAILQ_FOREACH(wp, &w->panes, entry) {
666 		if (wp == w->active)
667 			break;
668 		current++;
669 	}
670 
671 	if (current < visible) {
672 		start = 0;
673 		end = visible;
674 	} else if (current >= total - visible) {
675 		start = total - visible;
676 		end = total;
677 	} else {
678 		start = current - (visible / 2);
679 		end = start + visible;
680 	}
681 
682 	if (data->offset < -(int)start)
683 		data->offset = -(int)start;
684 	if (data->offset > (int)(total - end))
685 		data->offset = (int)(total - end);
686 	start += data->offset;
687 	end += data->offset;
688 
689 	left = (start != 0);
690 	right = (end != total);
691 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
692 		left = right = 0;
693 	if (left && right) {
694 		each = (sx - 6) / visible;
695 		remaining = (sx - 6) - (visible * each);
696 	} else if (left || right) {
697 		each = (sx - 3) / visible;
698 		remaining = (sx - 3) - (visible * each);
699 	} else {
700 		each = sx / visible;
701 		remaining = sx - (visible * each);
702 	}
703 	if (each == 0)
704 		return;
705 
706 	if (left) {
707 		data->left = cx + 2;
708 		screen_write_cursormove(ctx, cx + 2, cy, 0);
709 		screen_write_vline(ctx, sy, 0, 0);
710 		screen_write_cursormove(ctx, cx, cy + sy / 2, 0);
711 		screen_write_puts(ctx, &grid_default_cell, "<");
712 	} else
713 		data->left = -1;
714 	if (right) {
715 		data->right = cx + sx - 3;
716 		screen_write_cursormove(ctx, cx + sx - 3, cy, 0);
717 		screen_write_vline(ctx, sy, 0, 0);
718 		screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0);
719 		screen_write_puts(ctx, &grid_default_cell, ">");
720 	} else
721 		data->right = -1;
722 
723 	data->start = start;
724 	data->end = end;
725 	data->each = each;
726 
727 	i = loop = 0;
728 	TAILQ_FOREACH(wp, &w->panes, entry) {
729 		if (loop == end)
730 			break;
731 		if (loop < start) {
732 			loop++;
733 			continue;
734 		}
735 
736 		if (wp == w->active)
737 			gc.fg = active_colour;
738 		else
739 			gc.fg = colour;
740 
741 		if (left)
742 			offset = 3 + (i * each);
743 		else
744 			offset = (i * each);
745 		if (loop == end - 1)
746 			width = each + remaining;
747 		else
748 			width = each - 1;
749 
750 		screen_write_cursormove(ctx, cx + offset, cy, 0);
751 		screen_write_preview(ctx, &wp->base, width, sy);
752 
753 		if (window_pane_index(wp, &pane_idx) != 0)
754 			pane_idx = loop;
755 		xasprintf(&label, " %u ", pane_idx);
756 		window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc,
757 		    label);
758 		free(label);
759 
760 		if (loop != end - 1) {
761 			screen_write_cursormove(ctx, cx + offset + width, cy, 0);
762 			screen_write_vline(ctx, sy, 0, 0);
763 		}
764 		loop++;
765 
766 		i++;
767 	}
768 }
769 
770 static void
771 window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx,
772     u_int sx, u_int sy)
773 {
774 	struct window_tree_itemdata	*item = itemdata;
775 	struct session			*sp;
776 	struct winlink			*wlp;
777 	struct window_pane		*wp;
778 
779 	window_tree_pull_item(item, &sp, &wlp, &wp);
780 	if (wp == NULL)
781 		return;
782 
783 	switch (item->type) {
784 	case WINDOW_TREE_NONE:
785 		break;
786 	case WINDOW_TREE_SESSION:
787 		window_tree_draw_session(modedata, sp, ctx, sx, sy);
788 		break;
789 	case WINDOW_TREE_WINDOW:
790 		window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy);
791 		break;
792 	case WINDOW_TREE_PANE:
793 		screen_write_preview(ctx, &wp->base, sx, sy);
794 		break;
795 	}
796 }
797 
798 static int
799 window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
800 {
801 	struct window_tree_itemdata	*item = itemdata;
802 	struct session			*s;
803 	struct winlink			*wl;
804 	struct window_pane		*wp;
805 	char				*cmd;
806 	int				 retval;
807 
808 	window_tree_pull_item(item, &s, &wl, &wp);
809 
810 	switch (item->type) {
811 	case WINDOW_TREE_NONE:
812 		return (0);
813 	case WINDOW_TREE_SESSION:
814 		if (s == NULL)
815 			return (0);
816 		return (strstr(s->name, ss) != NULL);
817 	case WINDOW_TREE_WINDOW:
818 		if (s == NULL || wl == NULL)
819 			return (0);
820 		return (strstr(wl->window->name, ss) != NULL);
821 	case WINDOW_TREE_PANE:
822 		if (s == NULL || wl == NULL || wp == NULL)
823 			break;
824 		cmd = osdep_get_name(wp->fd, wp->tty);
825 		if (cmd == NULL || *cmd == '\0')
826 			return (0);
827 		retval = (strstr(cmd, ss) != NULL);
828 		free(cmd);
829 		return retval;
830 	}
831 	return (0);
832 }
833 
834 static void
835 window_tree_menu(void *modedata, struct client *c, key_code key)
836 {
837 	struct window_tree_modedata	*data = modedata;
838 	struct window_pane		*wp = data->wp;
839 	struct window_mode_entry	*wme;
840 
841 	wme = TAILQ_FIRST(&wp->modes);
842 	if (wme == NULL || wme->data != modedata)
843 		return;
844 	window_tree_key(wme, c, NULL, NULL, key, NULL);
845 }
846 
847 static struct screen *
848 window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
849     struct args *args)
850 {
851 	struct window_pane		*wp = wme->wp;
852 	struct window_tree_modedata	*data;
853 	struct screen			*s;
854 
855 	wme->data = data = xcalloc(1, sizeof *data);
856 	data->wp = wp;
857 	data->references = 1;
858 
859 	if (args_has(args, 's'))
860 		data->type = WINDOW_TREE_SESSION;
861 	else if (args_has(args, 'w'))
862 		data->type = WINDOW_TREE_WINDOW;
863 	else
864 		data->type = WINDOW_TREE_PANE;
865 	memcpy(&data->fs, fs, sizeof data->fs);
866 
867 	if (args == NULL || !args_has(args, 'F'))
868 		data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
869 	else
870 		data->format = xstrdup(args_get(args, 'F'));
871 	if (args == NULL || args->argc == 0)
872 		data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
873 	else
874 		data->command = xstrdup(args->argv[0]);
875 	data->squash_groups = !args_has(args, 'G');
876 
877 	data->data = mode_tree_start(wp, args, window_tree_build,
878 	    window_tree_draw, window_tree_search, window_tree_menu, data,
879 	    window_tree_menu_items, window_tree_sort_list,
880 	    nitems(window_tree_sort_list), &s);
881 	mode_tree_zoom(data->data, args);
882 
883 	mode_tree_build(data->data);
884 	mode_tree_draw(data->data);
885 
886 	data->type = WINDOW_TREE_NONE;
887 
888 	return (s);
889 }
890 
891 static void
892 window_tree_destroy(struct window_tree_modedata *data)
893 {
894 	u_int	i;
895 
896 	if (--data->references != 0)
897 		return;
898 
899 	for (i = 0; i < data->item_size; i++)
900 		window_tree_free_item(data->item_list[i]);
901 	free(data->item_list);
902 
903 	free(data->format);
904 	free(data->command);
905 
906 	free(data);
907 }
908 
909 static void
910 window_tree_free(struct window_mode_entry *wme)
911 {
912 	struct window_tree_modedata *data = wme->data;
913 
914 	if (data == NULL)
915 		return;
916 
917 	data->dead = 1;
918 	mode_tree_free(data->data);
919 	window_tree_destroy(data);
920 }
921 
922 static void
923 window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
924 {
925 	struct window_tree_modedata	*data = wme->data;
926 
927 	mode_tree_resize(data->data, sx, sy);
928 }
929 
930 static char *
931 window_tree_get_target(struct window_tree_itemdata *item,
932     struct cmd_find_state *fs)
933 {
934 	struct session		*s;
935 	struct winlink		*wl;
936 	struct window_pane	*wp;
937 	char			*target;
938 
939 	window_tree_pull_item(item, &s, &wl, &wp);
940 
941 	target = NULL;
942 	switch (item->type) {
943 	case WINDOW_TREE_NONE:
944 		break;
945 	case WINDOW_TREE_SESSION:
946 		if (s == NULL)
947 			break;
948 		xasprintf(&target, "=%s:", s->name);
949 		break;
950 	case WINDOW_TREE_WINDOW:
951 		if (s == NULL || wl == NULL)
952 			break;
953 		xasprintf(&target, "=%s:%u.", s->name, wl->idx);
954 		break;
955 	case WINDOW_TREE_PANE:
956 		if (s == NULL || wl == NULL || wp == NULL)
957 			break;
958 		xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
959 		break;
960 	}
961 	if (target == NULL)
962 		cmd_find_clear_state(fs, 0);
963 	else
964 		cmd_find_from_winlink_pane(fs, wl, wp, 0);
965 	return (target);
966 }
967 
968 static void
969 window_tree_command_each(void* modedata, void* itemdata, struct client *c,
970     __unused key_code key)
971 {
972 	struct window_tree_modedata	*data = modedata;
973 	struct window_tree_itemdata	*item = itemdata;
974 	char				*name;
975 	struct cmd_find_state		 fs;
976 
977 	name = window_tree_get_target(item, &fs);
978 	if (name != NULL)
979 		mode_tree_run_command(c, &fs, data->entered, name);
980 	free(name);
981 }
982 
983 static enum cmd_retval
984 window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
985 {
986 	struct window_tree_modedata	*data = modedata;
987 
988 	if (!data->dead) {
989 		mode_tree_build(data->data);
990 		mode_tree_draw(data->data);
991 		data->wp->flags |= PANE_REDRAW;
992 	}
993 	window_tree_destroy(data);
994 	return (CMD_RETURN_NORMAL);
995 }
996 
997 static int
998 window_tree_command_callback(struct client *c, void *modedata, const char *s,
999     __unused int done)
1000 {
1001 	struct window_tree_modedata	*data = modedata;
1002 
1003 	if (s == NULL || *s == '\0' || data->dead)
1004 		return (0);
1005 
1006 	data->entered = s;
1007 	mode_tree_each_tagged(data->data, window_tree_command_each, c,
1008 	    KEYC_NONE, 1);
1009 	data->entered = NULL;
1010 
1011 	data->references++;
1012 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
1013 
1014 	return (0);
1015 }
1016 
1017 static void
1018 window_tree_command_free(void *modedata)
1019 {
1020 	struct window_tree_modedata	*data = modedata;
1021 
1022 	window_tree_destroy(data);
1023 }
1024 
1025 static void
1026 window_tree_kill_each(__unused void* modedata, void* itemdata,
1027     __unused struct client *c, __unused key_code key)
1028 {
1029 	struct window_tree_itemdata	*item = itemdata;
1030 	struct session			*s;
1031 	struct winlink			*wl;
1032 	struct window_pane		*wp;
1033 
1034 	window_tree_pull_item(item, &s, &wl, &wp);
1035 
1036 	switch (item->type) {
1037 	case WINDOW_TREE_NONE:
1038 		break;
1039 	case WINDOW_TREE_SESSION:
1040 		if (s != NULL) {
1041 			server_destroy_session(s);
1042 			session_destroy(s, 1, __func__);
1043 		}
1044 		break;
1045 	case WINDOW_TREE_WINDOW:
1046 		if (wl != NULL)
1047 			server_kill_window(wl->window);
1048 		break;
1049 	case WINDOW_TREE_PANE:
1050 		if (wp != NULL)
1051 			server_kill_pane(wp);
1052 		break;
1053 	}
1054 }
1055 
1056 static int
1057 window_tree_kill_current_callback(struct client *c, void *modedata,
1058     const char *s, __unused int done)
1059 {
1060 	struct window_tree_modedata	*data = modedata;
1061 	struct mode_tree_data		*mtd = data->data;
1062 
1063 	if (s == NULL || *s == '\0' || data->dead)
1064 		return (0);
1065 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
1066 		return (0);
1067 
1068 	window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE);
1069 
1070 	data->references++;
1071 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
1072 
1073 	return (0);
1074 }
1075 
1076 static int
1077 window_tree_kill_tagged_callback(struct client *c, void *modedata,
1078     const char *s, __unused int done)
1079 {
1080 	struct window_tree_modedata	*data = modedata;
1081 	struct mode_tree_data		*mtd = data->data;
1082 
1083 	if (s == NULL || *s == '\0' || data->dead)
1084 		return (0);
1085 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
1086 		return (0);
1087 
1088 	mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1);
1089 
1090 	data->references++;
1091 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
1092 
1093 	return (0);
1094 }
1095 
1096 static key_code
1097 window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x,
1098     struct window_tree_itemdata *item)
1099 {
1100 	struct session		*s;
1101 	struct winlink		*wl;
1102 	struct window_pane	*wp;
1103 	u_int			 loop;
1104 
1105 	if (key != KEYC_MOUSEDOWN1_PANE)
1106 		return (KEYC_NONE);
1107 
1108 	if (data->left != -1 && x <= (u_int)data->left)
1109 		return ('<');
1110 	if (data->right != -1 && x >= (u_int)data->right)
1111 		return ('>');
1112 
1113 	if (data->left != -1)
1114 		x -= data->left;
1115 	else if (x != 0)
1116 		x--;
1117 	if (x == 0 || data->end == 0)
1118 		x = 0;
1119 	else {
1120 		x = x / data->each;
1121 		if (data->start + x >= data->end)
1122 			x = data->end - 1;
1123 	}
1124 
1125 	window_tree_pull_item(item, &s, &wl, &wp);
1126 	if (item->type == WINDOW_TREE_SESSION) {
1127 		if (s == NULL)
1128 			return (KEYC_NONE);
1129 		mode_tree_expand_current(data->data);
1130 		loop = 0;
1131 		RB_FOREACH(wl, winlinks, &s->windows) {
1132 			if (loop == data->start + x)
1133 				break;
1134 			loop++;
1135 		}
1136 		if (wl != NULL)
1137 			mode_tree_set_current(data->data, (uintptr_t)wl);
1138 		return ('\r');
1139 	}
1140 	if (item->type == WINDOW_TREE_WINDOW) {
1141 		if (wl == NULL)
1142 			return (KEYC_NONE);
1143 		mode_tree_expand_current(data->data);
1144 		loop = 0;
1145 		TAILQ_FOREACH(wp, &wl->window->panes, entry) {
1146 			if (loop == data->start + x)
1147 				break;
1148 			loop++;
1149 		}
1150 		if (wp != NULL)
1151 			mode_tree_set_current(data->data, (uintptr_t)wp);
1152 		return ('\r');
1153 	}
1154 	return (KEYC_NONE);
1155 }
1156 
1157 static void
1158 window_tree_key(struct window_mode_entry *wme, struct client *c,
1159     __unused struct session *s, __unused struct winlink *wl, key_code key,
1160     struct mouse_event *m)
1161 {
1162 	struct window_pane		*wp = wme->wp;
1163 	struct window_tree_modedata	*data = wme->data;
1164 	struct window_tree_itemdata	*item, *new_item;
1165 	char				*name, *prompt = NULL;
1166 	struct cmd_find_state		 fs;
1167 	int				 finished;
1168 	u_int				 tagged, x, y, idx;
1169 	struct session			*ns;
1170 	struct winlink			*nwl;
1171 	struct window_pane		*nwp;
1172 
1173 	item = mode_tree_get_current(data->data);
1174 	finished = mode_tree_key(data->data, c, &key, m, &x, &y);
1175 	if (item != (new_item = mode_tree_get_current(data->data))) {
1176 		item = new_item;
1177 		data->offset = 0;
1178 	}
1179 	if (KEYC_IS_MOUSE(key) && m != NULL)
1180 		key = window_tree_mouse(data, key, x, item);
1181 	switch (key) {
1182 	case '<':
1183 		data->offset--;
1184 		break;
1185 	case '>':
1186 		data->offset++;
1187 		break;
1188 	case 'x':
1189 		window_tree_pull_item(item, &ns, &nwl, &nwp);
1190 		switch (item->type) {
1191 		case WINDOW_TREE_NONE:
1192 			break;
1193 		case WINDOW_TREE_SESSION:
1194 			if (ns == NULL)
1195 				break;
1196 			xasprintf(&prompt, "Kill session %s? ", ns->name);
1197 			break;
1198 		case WINDOW_TREE_WINDOW:
1199 			if (nwl == NULL)
1200 				break;
1201 			xasprintf(&prompt, "Kill window %u? ", nwl->idx);
1202 			break;
1203 		case WINDOW_TREE_PANE:
1204 			if (nwp == NULL || window_pane_index(nwp, &idx) != 0)
1205 				break;
1206 			xasprintf(&prompt, "Kill pane %u? ", idx);
1207 			break;
1208 		}
1209 		if (prompt == NULL)
1210 			break;
1211 		data->references++;
1212 		status_prompt_set(c, prompt, "",
1213 		    window_tree_kill_current_callback, window_tree_command_free,
1214 		    data, PROMPT_SINGLE|PROMPT_NOFORMAT);
1215 		free(prompt);
1216 		break;
1217 	case 'X':
1218 		tagged = mode_tree_count_tagged(data->data);
1219 		if (tagged == 0)
1220 			break;
1221 		xasprintf(&prompt, "Kill %u tagged? ", tagged);
1222 		data->references++;
1223 		status_prompt_set(c, prompt, "",
1224 		    window_tree_kill_tagged_callback, window_tree_command_free,
1225 		    data, PROMPT_SINGLE|PROMPT_NOFORMAT);
1226 		free(prompt);
1227 		break;
1228 	case ':':
1229 		tagged = mode_tree_count_tagged(data->data);
1230 		if (tagged != 0)
1231 			xasprintf(&prompt, "(%u tagged) ", tagged);
1232 		else
1233 			xasprintf(&prompt, "(current) ");
1234 		data->references++;
1235 		status_prompt_set(c, prompt, "", window_tree_command_callback,
1236 		    window_tree_command_free, data, PROMPT_NOFORMAT);
1237 		free(prompt);
1238 		break;
1239 	case '\r':
1240 		name = window_tree_get_target(item, &fs);
1241 		if (name != NULL)
1242 			mode_tree_run_command(c, NULL, data->command, name);
1243 		finished = 1;
1244 		free(name);
1245 		break;
1246 	}
1247 	if (finished)
1248 		window_pane_reset_mode(wp);
1249 	else {
1250 		mode_tree_draw(data->data);
1251 		wp->flags |= PANE_REDRAW;
1252 	}
1253 }
1254