xref: /openbsd/usr.bin/tmux/window-tree.c (revision 2b7e51f7)
1*2b7e51f7Snicm /* $OpenBSD: window-tree.c,v 1.26 2017/11/02 22:00:42 nicm Exp $ */
2a42faf7dSnicm 
3a42faf7dSnicm /*
4a42faf7dSnicm  * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
5a42faf7dSnicm  *
6a42faf7dSnicm  * Permission to use, copy, modify, and distribute this software for any
7a42faf7dSnicm  * purpose with or without fee is hereby granted, provided that the above
8a42faf7dSnicm  * copyright notice and this permission notice appear in all copies.
9a42faf7dSnicm  *
10a42faf7dSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a42faf7dSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a42faf7dSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a42faf7dSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a42faf7dSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15a42faf7dSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16a42faf7dSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a42faf7dSnicm  */
18a42faf7dSnicm 
19a42faf7dSnicm #include <sys/types.h>
20a42faf7dSnicm 
21a42faf7dSnicm #include <stdlib.h>
22a42faf7dSnicm #include <string.h>
23a42faf7dSnicm 
24a42faf7dSnicm #include "tmux.h"
25a42faf7dSnicm 
26a42faf7dSnicm static struct screen	*window_tree_init(struct window_pane *,
27a42faf7dSnicm 			     struct cmd_find_state *, struct args *);
28a42faf7dSnicm static void		 window_tree_free(struct window_pane *);
29a42faf7dSnicm static void		 window_tree_resize(struct window_pane *, u_int, u_int);
30a42faf7dSnicm static void		 window_tree_key(struct window_pane *,
31a42faf7dSnicm 			     struct client *, struct session *, key_code,
32a42faf7dSnicm 			     struct mouse_event *);
33a42faf7dSnicm 
34a42faf7dSnicm #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'"
35a42faf7dSnicm 
36bf38e336Snicm #define WINDOW_TREE_DEFAULT_FORMAT \
37bf38e336Snicm 	"#{?pane_format," \
38bf38e336Snicm 		"#{pane_current_command} \"#{pane_title}\"" \
39bf38e336Snicm 	"," \
40bf38e336Snicm 		"#{?window_format," \
41bf38e336Snicm 			"#{window_name}#{window_flags} " \
42bf38e336Snicm 			"(#{window_panes} panes)" \
43bf38e336Snicm 			"#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \
44bf38e336Snicm 		"," \
45bf38e336Snicm 			"#{session_windows} windows" \
46988c4c87Snicm 			"#{?session_grouped, " \
4734f4cdc8Snicm 				"(group #{session_group}: " \
4834f4cdc8Snicm 				"#{session_group_list})," \
49988c4c87Snicm 			"}" \
50bf38e336Snicm 			"#{?session_attached, (attached),}" \
51bf38e336Snicm 		"}" \
52bf38e336Snicm 	"}"
53bf38e336Snicm 
54a42faf7dSnicm const struct window_mode window_tree_mode = {
55a42faf7dSnicm 	.name = "tree-mode",
56a42faf7dSnicm 
57a42faf7dSnicm 	.init = window_tree_init,
58a42faf7dSnicm 	.free = window_tree_free,
59a42faf7dSnicm 	.resize = window_tree_resize,
60a42faf7dSnicm 	.key = window_tree_key,
61a42faf7dSnicm };
62a42faf7dSnicm 
63a42faf7dSnicm enum window_tree_sort_type {
64a42faf7dSnicm 	WINDOW_TREE_BY_INDEX,
65a42faf7dSnicm 	WINDOW_TREE_BY_NAME,
66a42faf7dSnicm 	WINDOW_TREE_BY_TIME,
67a42faf7dSnicm };
68a42faf7dSnicm static const char *window_tree_sort_list[] = {
69a42faf7dSnicm 	"index",
70a42faf7dSnicm 	"name",
71a42faf7dSnicm 	"time"
72a42faf7dSnicm };
73a42faf7dSnicm 
74a42faf7dSnicm enum window_tree_type {
75a42faf7dSnicm 	WINDOW_TREE_NONE,
76a42faf7dSnicm 	WINDOW_TREE_SESSION,
77a42faf7dSnicm 	WINDOW_TREE_WINDOW,
78a42faf7dSnicm 	WINDOW_TREE_PANE,
79a42faf7dSnicm };
80a42faf7dSnicm 
81a42faf7dSnicm struct window_tree_itemdata {
82a42faf7dSnicm 	enum window_tree_type	type;
83a42faf7dSnicm 	int			session;
84a42faf7dSnicm 	int			winlink;
85a42faf7dSnicm 	int			pane;
86a42faf7dSnicm };
87a42faf7dSnicm 
88a42faf7dSnicm struct window_tree_modedata {
89a42faf7dSnicm 	struct window_pane		 *wp;
90a42faf7dSnicm 	int				  dead;
91a42faf7dSnicm 	int				  references;
92a42faf7dSnicm 
93a42faf7dSnicm 	struct mode_tree_data		 *data;
94bf38e336Snicm 	char				 *format;
95a42faf7dSnicm 	char				 *command;
96988c4c87Snicm 	int				  squash_groups;
97a42faf7dSnicm 
98a42faf7dSnicm 	struct window_tree_itemdata	**item_list;
99a42faf7dSnicm 	u_int				  item_size;
100a42faf7dSnicm 
101a42faf7dSnicm 	const char			 *entered;
102a42faf7dSnicm 
103a42faf7dSnicm 	struct cmd_find_state		  fs;
104a42faf7dSnicm 	enum window_tree_type		  type;
10505d44586Snicm 
10605d44586Snicm 	int				  offset;
107a42faf7dSnicm };
108a42faf7dSnicm 
109a42faf7dSnicm static void
110a42faf7dSnicm window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
111a42faf7dSnicm     struct winlink **wlp, struct window_pane **wp)
112a42faf7dSnicm {
113a42faf7dSnicm 	*wp = NULL;
114a42faf7dSnicm 	*wlp = NULL;
115a42faf7dSnicm 	*sp = session_find_by_id(item->session);
116a42faf7dSnicm 	if (*sp == NULL)
117a42faf7dSnicm 		return;
118a42faf7dSnicm 	if (item->type == WINDOW_TREE_SESSION) {
119a42faf7dSnicm 		*wlp = (*sp)->curw;
120a42faf7dSnicm 		*wp = (*wlp)->window->active;
121a42faf7dSnicm 		return;
122a42faf7dSnicm 	}
123a42faf7dSnicm 
124a42faf7dSnicm 	*wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
125a42faf7dSnicm 	if (*wlp == NULL) {
126a42faf7dSnicm 		*sp = NULL;
127a42faf7dSnicm 		return;
128a42faf7dSnicm 	}
129a42faf7dSnicm 	if (item->type == WINDOW_TREE_WINDOW) {
130a42faf7dSnicm 		*wp = (*wlp)->window->active;
131a42faf7dSnicm 		return;
132a42faf7dSnicm 	}
133a42faf7dSnicm 
134a42faf7dSnicm 	*wp = window_pane_find_by_id(item->pane);
135a42faf7dSnicm 	if (!window_has_pane((*wlp)->window, *wp))
136a42faf7dSnicm 		*wp = NULL;
137a42faf7dSnicm 	if (*wp == NULL) {
138a42faf7dSnicm 		*sp = NULL;
139a42faf7dSnicm 		*wlp = NULL;
140a42faf7dSnicm 		return;
141a42faf7dSnicm 	}
142a42faf7dSnicm }
143a42faf7dSnicm 
144a42faf7dSnicm static struct window_tree_itemdata *
145a42faf7dSnicm window_tree_add_item(struct window_tree_modedata *data)
146a42faf7dSnicm {
147a42faf7dSnicm 	struct window_tree_itemdata	*item;
148a42faf7dSnicm 
149a42faf7dSnicm 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
150a42faf7dSnicm 	    sizeof *data->item_list);
151a42faf7dSnicm 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
152a42faf7dSnicm 	return (item);
153a42faf7dSnicm }
154a42faf7dSnicm 
155a42faf7dSnicm static void
156a42faf7dSnicm window_tree_free_item(struct window_tree_itemdata *item)
157a42faf7dSnicm {
158a42faf7dSnicm 	free(item);
159a42faf7dSnicm }
160a42faf7dSnicm 
161a42faf7dSnicm static int
162a42faf7dSnicm window_tree_cmp_session_name(const void *a0, const void *b0)
163a42faf7dSnicm {
164a42faf7dSnicm 	const struct session *const *a = a0;
165a42faf7dSnicm 	const struct session *const *b = b0;
166a42faf7dSnicm 
167a42faf7dSnicm 	return (strcmp((*a)->name, (*b)->name));
168a42faf7dSnicm }
169a42faf7dSnicm 
170a42faf7dSnicm static int
171a42faf7dSnicm window_tree_cmp_session_time(const void *a0, const void *b0)
172a42faf7dSnicm {
173a42faf7dSnicm 	const struct session *const *a = a0;
174a42faf7dSnicm 	const struct session *const *b = b0;
175a42faf7dSnicm 
176a42faf7dSnicm 	if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >))
177a42faf7dSnicm 		return (-1);
178a42faf7dSnicm 	if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <))
179a42faf7dSnicm 		return (1);
180a42faf7dSnicm 	return (strcmp((*a)->name, (*b)->name));
181a42faf7dSnicm }
182a42faf7dSnicm 
183a42faf7dSnicm static int
184a42faf7dSnicm window_tree_cmp_window_name(const void *a0, const void *b0)
185a42faf7dSnicm {
186a42faf7dSnicm 	const struct winlink *const *a = a0;
187a42faf7dSnicm 	const struct winlink *const *b = b0;
188a42faf7dSnicm 
189a42faf7dSnicm 	return (strcmp((*a)->window->name, (*b)->window->name));
190a42faf7dSnicm }
191a42faf7dSnicm 
192a42faf7dSnicm static int
193a42faf7dSnicm window_tree_cmp_window_time(const void *a0, const void *b0)
194a42faf7dSnicm {
195a42faf7dSnicm 	const struct winlink *const *a = a0;
196a42faf7dSnicm 	const struct winlink *const *b = b0;
197a42faf7dSnicm 
198a42faf7dSnicm 	if (timercmp(&(*a)->window->activity_time,
199a42faf7dSnicm 	    &(*b)->window->activity_time, >))
200a42faf7dSnicm 		return (-1);
201a42faf7dSnicm 	if (timercmp(&(*a)->window->activity_time,
202a42faf7dSnicm 	    &(*b)->window->activity_time, <))
203a42faf7dSnicm 		return (1);
204a42faf7dSnicm 	return (strcmp((*a)->window->name, (*b)->window->name));
205a42faf7dSnicm }
206a42faf7dSnicm 
207a42faf7dSnicm static int
208a42faf7dSnicm window_tree_cmp_pane_time(const void *a0, const void *b0)
209a42faf7dSnicm {
210a42faf7dSnicm 	const struct window_pane *const *a = a0;
211a42faf7dSnicm 	const struct window_pane *const *b = b0;
212a42faf7dSnicm 
213a42faf7dSnicm 	if ((*a)->active_point < (*b)->active_point)
214a42faf7dSnicm 		return (-1);
215a42faf7dSnicm 	if ((*a)->active_point > (*b)->active_point)
216a42faf7dSnicm 		return (1);
217a42faf7dSnicm 	return (0);
218a42faf7dSnicm }
219a42faf7dSnicm 
220a42faf7dSnicm static void
221a42faf7dSnicm window_tree_build_pane(struct session *s, struct winlink *wl,
222a42faf7dSnicm     struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
223a42faf7dSnicm {
224a42faf7dSnicm 	struct window_tree_modedata	*data = modedata;
225a42faf7dSnicm 	struct window_tree_itemdata	*item;
226a42faf7dSnicm 	char				*name, *text;
227a42faf7dSnicm 	u_int				 idx;
228a42faf7dSnicm 
229a42faf7dSnicm 	window_pane_index(wp, &idx);
230a42faf7dSnicm 
231a42faf7dSnicm 	item = window_tree_add_item(data);
232a42faf7dSnicm 	item->type = WINDOW_TREE_PANE;
233a42faf7dSnicm 	item->session = s->id;
234a42faf7dSnicm 	item->winlink = wl->idx;
235a42faf7dSnicm 	item->pane = wp->id;
236a42faf7dSnicm 
237bf38e336Snicm 	text = format_single(NULL, data->format, NULL, s, wl, wp);
238a42faf7dSnicm 	xasprintf(&name, "%u", idx);
239a42faf7dSnicm 
240a42faf7dSnicm 	mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1);
241a42faf7dSnicm 	free(text);
242a42faf7dSnicm 	free(name);
243a42faf7dSnicm }
244a42faf7dSnicm 
245a42faf7dSnicm static int
246f5b43402Snicm window_tree_filter_pane(struct session *s, struct winlink *wl,
247f5b43402Snicm     struct window_pane *wp, const char *filter)
248f5b43402Snicm {
249f5b43402Snicm 	char	*cp;
250f5b43402Snicm 	int	 result;
251f5b43402Snicm 
252f5b43402Snicm 	if (filter == NULL)
253f5b43402Snicm 		return (1);
254f5b43402Snicm 
255f5b43402Snicm 	cp = format_single(NULL, filter, NULL, s, wl, wp);
256f5b43402Snicm 	result = format_true(cp);
257f5b43402Snicm 	free(cp);
258f5b43402Snicm 
259f5b43402Snicm 	return (result);
260f5b43402Snicm }
261f5b43402Snicm 
262f5b43402Snicm static int
263a42faf7dSnicm window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
264024c311aSnicm     u_int sort_type, struct mode_tree_item *parent, const char *filter)
265a42faf7dSnicm {
266a42faf7dSnicm 	struct window_tree_modedata	*data = modedata;
267a42faf7dSnicm 	struct window_tree_itemdata	*item;
268a42faf7dSnicm 	struct mode_tree_item		*mti;
269f5b43402Snicm 	char				*name, *text;
270a42faf7dSnicm 	struct window_pane		*wp, **l;
271a42faf7dSnicm 	u_int				 n, i;
272a42faf7dSnicm 	int				 expanded;
273a42faf7dSnicm 
274a42faf7dSnicm 	item = window_tree_add_item(data);
275a42faf7dSnicm 	item->type = WINDOW_TREE_WINDOW;
276a42faf7dSnicm 	item->session = s->id;
277a42faf7dSnicm 	item->winlink = wl->idx;
278a42faf7dSnicm 	item->pane = -1;
279a42faf7dSnicm 
280bf38e336Snicm 	text = format_single(NULL, data->format, NULL, s, wl, NULL);
281a42faf7dSnicm 	xasprintf(&name, "%u", wl->idx);
282a42faf7dSnicm 
283a42faf7dSnicm 	if (data->type == WINDOW_TREE_SESSION ||
284a42faf7dSnicm 	    data->type == WINDOW_TREE_WINDOW)
285a42faf7dSnicm 		expanded = 0;
286a42faf7dSnicm 	else
287a42faf7dSnicm 		expanded = 1;
288a42faf7dSnicm 	mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text,
289a42faf7dSnicm 	    expanded);
290a42faf7dSnicm 	free(text);
291a42faf7dSnicm 	free(name);
292a42faf7dSnicm 
293f5b43402Snicm 	wp = TAILQ_FIRST(&wl->window->panes);
294f5b43402Snicm 	if (TAILQ_NEXT(wp, entry) == NULL) {
295f5b43402Snicm 		if (!window_tree_filter_pane(s, wl, wp, filter))
296f5b43402Snicm 			goto empty;
2972920087aSnicm 		return (1);
298f5b43402Snicm 	}
2992920087aSnicm 
300a42faf7dSnicm 	l = NULL;
301a42faf7dSnicm 	n = 0;
3022920087aSnicm 
303a42faf7dSnicm 	TAILQ_FOREACH(wp, &wl->window->panes, entry) {
304f5b43402Snicm 		if (!window_tree_filter_pane(s, wl, wp, filter))
305a42faf7dSnicm 			continue;
306a42faf7dSnicm 		l = xreallocarray(l, n + 1, sizeof *l);
307a42faf7dSnicm 		l[n++] = wp;
308a42faf7dSnicm 	}
309f5b43402Snicm 	if (n == 0)
310f5b43402Snicm 		goto empty;
311a42faf7dSnicm 
312a42faf7dSnicm 	switch (sort_type) {
313a42faf7dSnicm 	case WINDOW_TREE_BY_INDEX:
314a42faf7dSnicm 		break;
315a42faf7dSnicm 	case WINDOW_TREE_BY_NAME:
316a42faf7dSnicm 		/* Panes don't have names, so leave in number order. */
317a42faf7dSnicm 		break;
318a42faf7dSnicm 	case WINDOW_TREE_BY_TIME:
319a42faf7dSnicm 		qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
320a42faf7dSnicm 		break;
321a42faf7dSnicm 	}
322a42faf7dSnicm 
323a42faf7dSnicm 	for (i = 0; i < n; i++)
324a42faf7dSnicm 		window_tree_build_pane(s, wl, l[i], modedata, mti);
325a42faf7dSnicm 	free(l);
326a42faf7dSnicm 	return (1);
327f5b43402Snicm 
328f5b43402Snicm empty:
329f5b43402Snicm 	window_tree_free_item(item);
330f5b43402Snicm 	data->item_size--;
331f5b43402Snicm 	mode_tree_remove(data->data, mti);
332f5b43402Snicm 	return (0);
333a42faf7dSnicm }
334a42faf7dSnicm 
335a42faf7dSnicm static void
336a42faf7dSnicm window_tree_build_session(struct session *s, void* modedata,
337024c311aSnicm     u_int sort_type, const char *filter)
338a42faf7dSnicm {
339a42faf7dSnicm 	struct window_tree_modedata	*data = modedata;
340a42faf7dSnicm 	struct window_tree_itemdata	*item;
341a42faf7dSnicm 	struct mode_tree_item		*mti;
342a42faf7dSnicm 	char				*text;
343a42faf7dSnicm 	struct winlink			*wl, **l;
344a42faf7dSnicm 	u_int				 n, i, empty;
345a42faf7dSnicm 	int				 expanded;
346a42faf7dSnicm 
347a42faf7dSnicm 	item = window_tree_add_item(data);
348a42faf7dSnicm 	item->type = WINDOW_TREE_SESSION;
349a42faf7dSnicm 	item->session = s->id;
350a42faf7dSnicm 	item->winlink = -1;
351a42faf7dSnicm 	item->pane = -1;
352a42faf7dSnicm 
353bf38e336Snicm 	text = format_single(NULL, data->format, NULL, s, NULL, NULL);
354a42faf7dSnicm 
355a42faf7dSnicm 	if (data->type == WINDOW_TREE_SESSION)
356a42faf7dSnicm 		expanded = 0;
357a42faf7dSnicm 	else
358a42faf7dSnicm 		expanded = 1;
359a42faf7dSnicm 	mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text,
360a42faf7dSnicm 	    expanded);
361a42faf7dSnicm 	free(text);
362a42faf7dSnicm 
363a42faf7dSnicm 	l = NULL;
364a42faf7dSnicm 	n = 0;
365a42faf7dSnicm 	RB_FOREACH(wl, winlinks, &s->windows) {
366a42faf7dSnicm 		l = xreallocarray(l, n + 1, sizeof *l);
367a42faf7dSnicm 		l[n++] = wl;
368a42faf7dSnicm 	}
369a42faf7dSnicm 	switch (sort_type) {
370a42faf7dSnicm 	case WINDOW_TREE_BY_INDEX:
371a42faf7dSnicm 		break;
372a42faf7dSnicm 	case WINDOW_TREE_BY_NAME:
373a42faf7dSnicm 		qsort(l, n, sizeof *l, window_tree_cmp_window_name);
374a42faf7dSnicm 		break;
375a42faf7dSnicm 	case WINDOW_TREE_BY_TIME:
376a42faf7dSnicm 		qsort(l, n, sizeof *l, window_tree_cmp_window_time);
377a42faf7dSnicm 		break;
378a42faf7dSnicm 	}
379a42faf7dSnicm 
380a42faf7dSnicm 	empty = 0;
381a42faf7dSnicm 	for (i = 0; i < n; i++) {
382a42faf7dSnicm 		if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
383024c311aSnicm 		    filter))
384a42faf7dSnicm 			empty++;
385a42faf7dSnicm 	}
386a42faf7dSnicm 	if (empty == n) {
387a42faf7dSnicm 		window_tree_free_item(item);
388a42faf7dSnicm 		data->item_size--;
389a42faf7dSnicm 		mode_tree_remove(data->data, mti);
390a42faf7dSnicm 	}
391a42faf7dSnicm 	free(l);
392a42faf7dSnicm }
393a42faf7dSnicm 
394a42faf7dSnicm static void
395024c311aSnicm window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
396024c311aSnicm     const char *filter)
397a42faf7dSnicm {
398a42faf7dSnicm 	struct window_tree_modedata	*data = modedata;
399a42faf7dSnicm 	struct session			*s, **l;
400f443747aSnicm 	struct session_group		*sg, *current;
401a42faf7dSnicm 	u_int				 n, i;
402a42faf7dSnicm 
403f443747aSnicm 	current = session_group_contains(data->fs.s);
404f443747aSnicm 
405a42faf7dSnicm 	for (i = 0; i < data->item_size; i++)
406a42faf7dSnicm 		window_tree_free_item(data->item_list[i]);
407a42faf7dSnicm 	free(data->item_list);
408a42faf7dSnicm 	data->item_list = NULL;
409a42faf7dSnicm 	data->item_size = 0;
410a42faf7dSnicm 
411a42faf7dSnicm 	l = NULL;
412a42faf7dSnicm 	n = 0;
413a42faf7dSnicm 	RB_FOREACH(s, sessions, &sessions) {
414988c4c87Snicm 		if (data->squash_groups &&
415f443747aSnicm 		    (sg = session_group_contains(s)) != NULL) {
416f443747aSnicm 			if ((sg == current && s != data->fs.s) ||
417f443747aSnicm 			    (sg != current && s != TAILQ_FIRST(&sg->sessions)))
418988c4c87Snicm 				continue;
419f443747aSnicm 		}
420a42faf7dSnicm 		l = xreallocarray(l, n + 1, sizeof *l);
421a42faf7dSnicm 		l[n++] = s;
422a42faf7dSnicm 	}
423a42faf7dSnicm 	switch (sort_type) {
424a42faf7dSnicm 	case WINDOW_TREE_BY_INDEX:
425a42faf7dSnicm 		break;
426a42faf7dSnicm 	case WINDOW_TREE_BY_NAME:
427a42faf7dSnicm 		qsort(l, n, sizeof *l, window_tree_cmp_session_name);
428a42faf7dSnicm 		break;
429a42faf7dSnicm 	case WINDOW_TREE_BY_TIME:
430a42faf7dSnicm 		qsort(l, n, sizeof *l, window_tree_cmp_session_time);
431a42faf7dSnicm 		break;
432a42faf7dSnicm 	}
433a42faf7dSnicm 
434a42faf7dSnicm 	for (i = 0; i < n; i++)
435024c311aSnicm 		window_tree_build_session(l[i], modedata, sort_type, filter);
436a42faf7dSnicm 	free(l);
437a42faf7dSnicm 
438a42faf7dSnicm 	switch (data->type) {
439a42faf7dSnicm 	case WINDOW_TREE_NONE:
440a42faf7dSnicm 		break;
441a42faf7dSnicm 	case WINDOW_TREE_SESSION:
442a42faf7dSnicm 		*tag = (uint64_t)data->fs.s;
443a42faf7dSnicm 		break;
444a42faf7dSnicm 	case WINDOW_TREE_WINDOW:
445a42faf7dSnicm 		*tag = (uint64_t)data->fs.wl;
446a42faf7dSnicm 		break;
447a42faf7dSnicm 	case WINDOW_TREE_PANE:
4489e3444daSnicm 		if (window_count_panes(data->fs.wl->window) == 1)
4499e3444daSnicm 			*tag = (uint64_t)data->fs.wl;
4509e3444daSnicm 		else
451a42faf7dSnicm 			*tag = (uint64_t)data->fs.wp;
452a42faf7dSnicm 		break;
453a42faf7dSnicm 	}
454a42faf7dSnicm }
455a42faf7dSnicm 
4560088ef99Snicm 
4570088ef99Snicm static void
4580088ef99Snicm window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
4590088ef99Snicm     u_int sx, u_int sy, const struct grid_cell *gc, const char *label)
4600088ef99Snicm {
4610088ef99Snicm 	size_t	 len;
4620088ef99Snicm 	u_int	 ox, oy;
4630088ef99Snicm 
4640088ef99Snicm 	len = strlen(label);
4650088ef99Snicm 	if (sx == 0 || sy == 1 || len > sx)
4660088ef99Snicm 		return;
4670088ef99Snicm 	ox = (sx - len + 1) / 2;
4680088ef99Snicm 	oy = (sy + 1) / 2;
4690088ef99Snicm 
4700088ef99Snicm 	if (ox > 1 && ox + len < sx - 1 && sy >= 3) {
4710088ef99Snicm 		screen_write_cursormove(ctx, px + ox - 1, py + oy - 1);
4720088ef99Snicm 		screen_write_box(ctx, len + 2, 3);
4730088ef99Snicm 	}
4740088ef99Snicm 	screen_write_cursormove(ctx, px + ox, py + oy);
4750088ef99Snicm 	screen_write_puts(ctx, gc, "%s", label);
4760088ef99Snicm }
4770088ef99Snicm 
478367b65a4Snicm static void
47905d44586Snicm window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
48005d44586Snicm     struct screen_write_ctx *ctx, u_int sx, u_int sy)
481367b65a4Snicm {
482367b65a4Snicm 	struct options		*oo = s->options;
483367b65a4Snicm 	struct winlink		*wl;
484367b65a4Snicm 	struct window		*w;
485*2b7e51f7Snicm 	u_int			 cx = ctx->s->cx, cy = ctx->s->cy;
48624a42803Snicm 	u_int			 loop, total, visible, each, width, offset;
48724a42803Snicm 	u_int			 current, start, end, remaining, i;
488367b65a4Snicm 	struct grid_cell	 gc;
48924a42803Snicm 	int			 colour, active_colour, left, right;
490367b65a4Snicm 	char			*label;
491367b65a4Snicm 
49224a42803Snicm 	total = winlink_count(&s->windows);
493367b65a4Snicm 
494367b65a4Snicm 	memcpy(&gc, &grid_default_cell, sizeof gc);
495367b65a4Snicm 	colour = options_get_number(oo, "display-panes-colour");
496367b65a4Snicm 	active_colour = options_get_number(oo, "display-panes-active-colour");
497367b65a4Snicm 
49824a42803Snicm 	if (sx / total < 24) {
49924a42803Snicm 		visible = sx / 24;
50024a42803Snicm 		if (visible == 0)
50124a42803Snicm 			visible = 1;
50224a42803Snicm 	} else
50324a42803Snicm 		visible = total;
504367b65a4Snicm 
50524a42803Snicm 	current = 0;
50624a42803Snicm 	RB_FOREACH(wl, winlinks, &s->windows) {
50724a42803Snicm 		if (wl == s->curw)
50824a42803Snicm 			break;
50924a42803Snicm 		current++;
51024a42803Snicm 	}
511367b65a4Snicm 
51224a42803Snicm 	if (current < visible) {
51324a42803Snicm 		start = 0;
51424a42803Snicm 		end = visible;
51524a42803Snicm 	} else if (current >= total - visible) {
51624a42803Snicm 		start = total - visible;
51724a42803Snicm 		end = total;
51824a42803Snicm 	} else {
51924a42803Snicm 		start = current - (visible / 2);
52024a42803Snicm 		end = start + visible;
52124a42803Snicm 	}
52224a42803Snicm 
52305d44586Snicm 	if (data->offset < -(int)start)
52405d44586Snicm 		data->offset = -(int)start;
52505d44586Snicm 	if (data->offset > (int)(total - end))
52605d44586Snicm 		data->offset = (int)(total - end);
52705d44586Snicm 	start += data->offset;
52805d44586Snicm 	end += data->offset;
52905d44586Snicm 
53024a42803Snicm 	left = (start != 0);
53124a42803Snicm 	right = (end != total);
53224a42803Snicm 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
53324a42803Snicm 		left = right = 0;
53424a42803Snicm 	if (left && right) {
53524a42803Snicm 		each = (sx - 6) / visible;
53624a42803Snicm 		remaining = (sx - 6) - (visible * each);
53724a42803Snicm 	} else if (left || right) {
53824a42803Snicm 		each = (sx - 3) / visible;
53924a42803Snicm 		remaining = (sx - 3) - (visible * each);
54024a42803Snicm 	} else {
54124a42803Snicm 		each = sx / visible;
54224a42803Snicm 		remaining = sx - (visible * each);
54324a42803Snicm 	}
544c224fd4bSnicm 	if (each == 0)
545367b65a4Snicm 		return;
546367b65a4Snicm 
54724a42803Snicm 	if (left) {
548*2b7e51f7Snicm 		screen_write_cursormove(ctx, cx + 2, cy);
54924a42803Snicm 		screen_write_vline(ctx, sy, 0, 0);
550*2b7e51f7Snicm 		screen_write_cursormove(ctx, cx, cy + sy / 2);
55124a42803Snicm 		screen_write_puts(ctx, &grid_default_cell, "<");
55224a42803Snicm 	}
55324a42803Snicm 	if (right) {
554*2b7e51f7Snicm 		screen_write_cursormove(ctx, cx + sx - 3, cy);
55524a42803Snicm 		screen_write_vline(ctx, sy, 0, 0);
556*2b7e51f7Snicm 		screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2);
55724a42803Snicm 		screen_write_puts(ctx, &grid_default_cell, ">");
55824a42803Snicm 	}
55924a42803Snicm 
56024a42803Snicm 	i = loop = 0;
56124a42803Snicm 	RB_FOREACH(wl, winlinks, &s->windows) {
56224a42803Snicm 		if (loop == end)
56324a42803Snicm 			break;
56424a42803Snicm 		if (loop < start) {
56524a42803Snicm 			loop++;
56624a42803Snicm 			continue;
56724a42803Snicm 		}
56824a42803Snicm 		w = wl->window;
56924a42803Snicm 
570367b65a4Snicm 		if (wl == s->curw)
571367b65a4Snicm 			gc.fg = active_colour;
572367b65a4Snicm 		else
573367b65a4Snicm 			gc.fg = colour;
57424a42803Snicm 
57524a42803Snicm 		if (left)
57624a42803Snicm 			offset = 3 + (i * each);
57724a42803Snicm 		else
57824a42803Snicm 			offset = (i * each);
57924a42803Snicm 		if (loop == end - 1)
58003471407Snicm 			width = each + remaining;
581367b65a4Snicm 		else
582367b65a4Snicm 			width = each - 1;
583367b65a4Snicm 
584*2b7e51f7Snicm 		screen_write_cursormove(ctx, cx + offset, cy);
585367b65a4Snicm 		screen_write_preview(ctx, &w->active->base, width, sy);
586367b65a4Snicm 
587367b65a4Snicm 		xasprintf(&label, " %u:%s ", wl->idx, w->name);
588367b65a4Snicm 		if (strlen(label) > width)
589367b65a4Snicm 			xasprintf(&label, " %u ", wl->idx);
590*2b7e51f7Snicm 		window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc,
591*2b7e51f7Snicm 		    label);
592367b65a4Snicm 		free(label);
593367b65a4Snicm 
59424a42803Snicm 		if (loop != end - 1) {
595*2b7e51f7Snicm 			screen_write_cursormove(ctx, cx + offset + width, cy);
596367b65a4Snicm 			screen_write_vline(ctx, sy, 0, 0);
597367b65a4Snicm 		}
59824a42803Snicm 		loop++;
59924a42803Snicm 
60024a42803Snicm 		i++;
601367b65a4Snicm 	}
602367b65a4Snicm }
603367b65a4Snicm 
604367b65a4Snicm static void
60505d44586Snicm window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
60605d44586Snicm     struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy)
607367b65a4Snicm {
608367b65a4Snicm 	struct options		*oo = s->options;
609367b65a4Snicm 	struct window_pane	*wp;
610*2b7e51f7Snicm 	u_int			 cx = ctx->s->cx, cy = ctx->s->cy;
61124a42803Snicm 	u_int			 loop, total, visible, each, width, offset;
61224a42803Snicm 	u_int			 current, start, end, remaining, i;
613367b65a4Snicm 	struct grid_cell	 gc;
6148807a41eSnicm 	int			 colour, active_colour, left, right, pane_idx;
615367b65a4Snicm 	char			*label;
616367b65a4Snicm 
61724a42803Snicm 	total = window_count_panes(w);
618367b65a4Snicm 
619367b65a4Snicm 	memcpy(&gc, &grid_default_cell, sizeof gc);
620367b65a4Snicm 	colour = options_get_number(oo, "display-panes-colour");
621367b65a4Snicm 	active_colour = options_get_number(oo, "display-panes-active-colour");
622367b65a4Snicm 
62324a42803Snicm 	if (sx / total < 24) {
62424a42803Snicm 		visible = sx / 24;
62524a42803Snicm 		if (visible == 0)
62624a42803Snicm 			visible = 1;
62724a42803Snicm 	} else
62824a42803Snicm 		visible = total;
629367b65a4Snicm 
63024a42803Snicm 	current = 0;
63124a42803Snicm 	TAILQ_FOREACH(wp, &w->panes, entry) {
63224a42803Snicm 		if (wp == w->active)
63324a42803Snicm 			break;
63424a42803Snicm 		current++;
63524a42803Snicm 	}
636367b65a4Snicm 
63724a42803Snicm 	if (current < visible) {
63824a42803Snicm 		start = 0;
63924a42803Snicm 		end = visible;
64024a42803Snicm 	} else if (current >= total - visible) {
64124a42803Snicm 		start = total - visible;
64224a42803Snicm 		end = total;
64324a42803Snicm 	} else {
64424a42803Snicm 		start = current - (visible / 2);
64524a42803Snicm 		end = start + visible;
64624a42803Snicm 	}
64724a42803Snicm 
64805d44586Snicm 	if (data->offset < -(int)start)
64905d44586Snicm 		data->offset = -(int)start;
65005d44586Snicm 	if (data->offset > (int)(total - end))
65105d44586Snicm 		data->offset = (int)(total - end);
65205d44586Snicm 	start += data->offset;
65305d44586Snicm 	end += data->offset;
65405d44586Snicm 
65524a42803Snicm 	left = (start != 0);
65624a42803Snicm 	right = (end != total);
65724a42803Snicm 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
65824a42803Snicm 		left = right = 0;
65924a42803Snicm 	if (left && right) {
66024a42803Snicm 		each = (sx - 6) / visible;
66124a42803Snicm 		remaining = (sx - 6) - (visible * each);
66224a42803Snicm 	} else if (left || right) {
66324a42803Snicm 		each = (sx - 3) / visible;
66424a42803Snicm 		remaining = (sx - 3) - (visible * each);
66524a42803Snicm 	} else {
66624a42803Snicm 		each = sx / visible;
66724a42803Snicm 		remaining = sx - (visible * each);
66824a42803Snicm 	}
669c224fd4bSnicm 	if (each == 0)
670367b65a4Snicm 		return;
671367b65a4Snicm 
67224a42803Snicm 	if (left) {
673*2b7e51f7Snicm 		screen_write_cursormove(ctx, cx + 2, cy);
67424a42803Snicm 		screen_write_vline(ctx, sy, 0, 0);
675*2b7e51f7Snicm 		screen_write_cursormove(ctx, cx, cy + sy / 2);
67624a42803Snicm 		screen_write_puts(ctx, &grid_default_cell, "<");
67724a42803Snicm 	}
67824a42803Snicm 	if (right) {
679*2b7e51f7Snicm 		screen_write_cursormove(ctx, cx + sx - 3, cy);
68024a42803Snicm 		screen_write_vline(ctx, sy, 0, 0);
681*2b7e51f7Snicm 		screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2);
68224a42803Snicm 		screen_write_puts(ctx, &grid_default_cell, ">");
68324a42803Snicm 	}
68424a42803Snicm 
68524a42803Snicm 	i = loop = 0;
68624a42803Snicm 	TAILQ_FOREACH(wp, &w->panes, entry) {
68724a42803Snicm 		if (loop == end)
68824a42803Snicm 			break;
68924a42803Snicm 		if (loop < start) {
69024a42803Snicm 			loop++;
69124a42803Snicm 			continue;
69224a42803Snicm 		}
69324a42803Snicm 
694367b65a4Snicm 		if (wp == w->active)
695367b65a4Snicm 			gc.fg = active_colour;
696367b65a4Snicm 		else
697367b65a4Snicm 			gc.fg = colour;
69824a42803Snicm 
69924a42803Snicm 		if (left)
70024a42803Snicm 			offset = 3 + (i * each);
70124a42803Snicm 		else
70224a42803Snicm 			offset = (i * each);
70324a42803Snicm 		if (loop == end - 1)
70403471407Snicm 			width = each + remaining;
705367b65a4Snicm 		else
706367b65a4Snicm 			width = each - 1;
707367b65a4Snicm 
708*2b7e51f7Snicm 		screen_write_cursormove(ctx, cx + offset, cy);
709367b65a4Snicm 		screen_write_preview(ctx, &wp->base, width, sy);
710367b65a4Snicm 
7118807a41eSnicm 		if (window_pane_index(wp, &pane_idx) != 0)
7128807a41eSnicm 			pane_idx = loop;
7138807a41eSnicm 		xasprintf(&label, " %u ", pane_idx);
714*2b7e51f7Snicm 		window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc,
715*2b7e51f7Snicm 		    label);
716367b65a4Snicm 		free(label);
717367b65a4Snicm 
71824a42803Snicm 		if (loop != end - 1) {
719*2b7e51f7Snicm 			screen_write_cursormove(ctx, cx + offset + width, cy);
720367b65a4Snicm 			screen_write_vline(ctx, sy, 0, 0);
721367b65a4Snicm 		}
72224a42803Snicm 		loop++;
72324a42803Snicm 
72424a42803Snicm 		i++;
725367b65a4Snicm 	}
726367b65a4Snicm }
727367b65a4Snicm 
728*2b7e51f7Snicm static void
729*2b7e51f7Snicm window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx,
730*2b7e51f7Snicm     u_int sx, u_int sy)
731a42faf7dSnicm {
732a42faf7dSnicm 	struct window_tree_itemdata	*item = itemdata;
733a42faf7dSnicm 	struct session			*sp;
734a42faf7dSnicm 	struct winlink			*wlp;
735a42faf7dSnicm 	struct window_pane		*wp;
736a42faf7dSnicm 
737a42faf7dSnicm 	window_tree_pull_item(item, &sp, &wlp, &wp);
738a42faf7dSnicm 	if (wp == NULL)
739*2b7e51f7Snicm 		return;
740a42faf7dSnicm 
741367b65a4Snicm 	switch (item->type) {
742367b65a4Snicm 	case WINDOW_TREE_NONE:
743*2b7e51f7Snicm 		break;
744367b65a4Snicm 	case WINDOW_TREE_SESSION:
745*2b7e51f7Snicm 		window_tree_draw_session(modedata, sp, ctx, sx, sy);
746367b65a4Snicm 		break;
747367b65a4Snicm 	case WINDOW_TREE_WINDOW:
748*2b7e51f7Snicm 		window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy);
749367b65a4Snicm 		break;
750367b65a4Snicm 	case WINDOW_TREE_PANE:
751*2b7e51f7Snicm 		screen_write_preview(ctx, &wp->base, sx, sy);
752367b65a4Snicm 		break;
753367b65a4Snicm 	}
754a42faf7dSnicm }
755a42faf7dSnicm 
756943a08b1Snicm static int
757943a08b1Snicm window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
758943a08b1Snicm {
759943a08b1Snicm 	struct window_tree_itemdata	*item = itemdata;
760943a08b1Snicm 	struct session			*s;
761943a08b1Snicm 	struct winlink			*wl;
762943a08b1Snicm 	struct window_pane		*wp;
763943a08b1Snicm 	const char			*cmd;
764943a08b1Snicm 
765943a08b1Snicm 	window_tree_pull_item(item, &s, &wl, &wp);
766943a08b1Snicm 
767943a08b1Snicm 	switch (item->type) {
768943a08b1Snicm 	case WINDOW_TREE_NONE:
769943a08b1Snicm 		return (0);
770943a08b1Snicm 	case WINDOW_TREE_SESSION:
771943a08b1Snicm 		if (s == NULL)
772943a08b1Snicm 			return (0);
773943a08b1Snicm 		return (strstr(s->name, ss) != NULL);
774943a08b1Snicm 	case WINDOW_TREE_WINDOW:
775943a08b1Snicm 		if (s == NULL || wl == NULL)
776943a08b1Snicm 			return (0);
777943a08b1Snicm 		return (strstr(wl->window->name, ss) != NULL);
778943a08b1Snicm 	case WINDOW_TREE_PANE:
779943a08b1Snicm 		if (s == NULL || wl == NULL || wp == NULL)
780943a08b1Snicm 			break;
781943a08b1Snicm 		cmd = get_proc_name(wp->fd, wp->tty);
782943a08b1Snicm 		if (cmd == NULL || *cmd == '\0')
783943a08b1Snicm 			return (0);
784943a08b1Snicm 		return (strstr(cmd, ss) != NULL);
785943a08b1Snicm 	}
786943a08b1Snicm 	return (0);
787943a08b1Snicm }
788943a08b1Snicm 
789a42faf7dSnicm static struct screen *
790a42faf7dSnicm window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
791a42faf7dSnicm     struct args *args)
792a42faf7dSnicm {
793a42faf7dSnicm 	struct window_tree_modedata	*data;
794a42faf7dSnicm 	struct screen			*s;
795a42faf7dSnicm 
796a42faf7dSnicm 	wp->modedata = data = xcalloc(1, sizeof *data);
797a42faf7dSnicm 
798a42faf7dSnicm 	if (args_has(args, 's'))
799a42faf7dSnicm 		data->type = WINDOW_TREE_SESSION;
800a42faf7dSnicm 	else if (args_has(args, 'w'))
801a42faf7dSnicm 		data->type = WINDOW_TREE_WINDOW;
802a42faf7dSnicm 	else
803a42faf7dSnicm 		data->type = WINDOW_TREE_PANE;
804a42faf7dSnicm 	memcpy(&data->fs, fs, sizeof data->fs);
805a42faf7dSnicm 
806a42faf7dSnicm 	data->wp = wp;
807a42faf7dSnicm 	data->references = 1;
808a42faf7dSnicm 
809bf38e336Snicm 	if (args == NULL || !args_has(args, 'F'))
810bf38e336Snicm 		data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
811bf38e336Snicm 	else
812bf38e336Snicm 		data->format = xstrdup(args_get(args, 'F'));
813a42faf7dSnicm 	if (args == NULL || args->argc == 0)
814a42faf7dSnicm 		data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
815a42faf7dSnicm 	else
816a42faf7dSnicm 		data->command = xstrdup(args->argv[0]);
817988c4c87Snicm 	data->squash_groups = !args_has(args, 'G');
818a42faf7dSnicm 
819b38aa712Snicm 	data->data = mode_tree_start(wp, args, window_tree_build,
820b38aa712Snicm 	    window_tree_draw, window_tree_search, data, window_tree_sort_list,
821a42faf7dSnicm 	    nitems(window_tree_sort_list), &s);
822a42faf7dSnicm 
823a42faf7dSnicm 	mode_tree_build(data->data);
824a42faf7dSnicm 	mode_tree_draw(data->data);
825a42faf7dSnicm 
826a42faf7dSnicm 	data->type = WINDOW_TREE_NONE;
827a42faf7dSnicm 
828a42faf7dSnicm 	return (s);
829a42faf7dSnicm }
830a42faf7dSnicm 
831a42faf7dSnicm static void
832a42faf7dSnicm window_tree_destroy(struct window_tree_modedata *data)
833a42faf7dSnicm {
834a42faf7dSnicm 	u_int	i;
835a42faf7dSnicm 
836a42faf7dSnicm 	if (--data->references != 0)
837a42faf7dSnicm 		return;
838a42faf7dSnicm 
839a42faf7dSnicm 	mode_tree_free(data->data);
840a42faf7dSnicm 
841a42faf7dSnicm 	for (i = 0; i < data->item_size; i++)
842a42faf7dSnicm 		window_tree_free_item(data->item_list[i]);
843a42faf7dSnicm 	free(data->item_list);
844a42faf7dSnicm 
845bf38e336Snicm 	free(data->format);
846a42faf7dSnicm 	free(data->command);
847bf38e336Snicm 
848a42faf7dSnicm 	free(data);
849a42faf7dSnicm }
850a42faf7dSnicm 
851a42faf7dSnicm static void
852a42faf7dSnicm window_tree_free(struct window_pane *wp)
853a42faf7dSnicm {
854a42faf7dSnicm 	struct window_tree_modedata *data = wp->modedata;
855a42faf7dSnicm 
856a42faf7dSnicm 	if (data == NULL)
857a42faf7dSnicm 		return;
858a42faf7dSnicm 
859a42faf7dSnicm 	data->dead = 1;
860a42faf7dSnicm 	window_tree_destroy(data);
861a42faf7dSnicm }
862a42faf7dSnicm 
863a42faf7dSnicm static void
864a42faf7dSnicm window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
865a42faf7dSnicm {
866a42faf7dSnicm 	struct window_tree_modedata	*data = wp->modedata;
867a42faf7dSnicm 
868a42faf7dSnicm 	mode_tree_resize(data->data, sx, sy);
869a42faf7dSnicm }
870a42faf7dSnicm 
871a42faf7dSnicm static char *
872a42faf7dSnicm window_tree_get_target(struct window_tree_itemdata *item,
873a42faf7dSnicm     struct cmd_find_state *fs)
874a42faf7dSnicm {
875a42faf7dSnicm 	struct session		*s;
876a42faf7dSnicm 	struct winlink		*wl;
877a42faf7dSnicm 	struct window_pane	*wp;
878a42faf7dSnicm 	char			*target;
879a42faf7dSnicm 
880a42faf7dSnicm 	window_tree_pull_item(item, &s, &wl, &wp);
881a42faf7dSnicm 
882a42faf7dSnicm 	target = NULL;
883a42faf7dSnicm 	switch (item->type) {
884a42faf7dSnicm 	case WINDOW_TREE_NONE:
885a42faf7dSnicm 		break;
886a42faf7dSnicm 	case WINDOW_TREE_SESSION:
887a42faf7dSnicm 		if (s == NULL)
888a42faf7dSnicm 			break;
889a42faf7dSnicm 		xasprintf(&target, "=%s:", s->name);
890a42faf7dSnicm 		break;
891a42faf7dSnicm 	case WINDOW_TREE_WINDOW:
892a42faf7dSnicm 		if (s == NULL || wl == NULL)
893a42faf7dSnicm 			break;
894a42faf7dSnicm 		xasprintf(&target, "=%s:%u.", s->name, wl->idx);
895a42faf7dSnicm 		break;
896a42faf7dSnicm 	case WINDOW_TREE_PANE:
897a42faf7dSnicm 		if (s == NULL || wl == NULL || wp == NULL)
898a42faf7dSnicm 			break;
899a42faf7dSnicm 		xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
900a42faf7dSnicm 		break;
901a42faf7dSnicm 	}
902a42faf7dSnicm 	if (target == NULL)
903a42faf7dSnicm 		cmd_find_clear_state(fs, 0);
904a42faf7dSnicm 	else
9050772530eSnicm 		cmd_find_from_winlink_pane(fs, wl, wp, 0);
906a42faf7dSnicm 	return (target);
907a42faf7dSnicm }
908a42faf7dSnicm 
909a42faf7dSnicm static void
910d7af2c28Snicm window_tree_command_each(void* modedata, void* itemdata, struct client *c,
911d7af2c28Snicm     __unused key_code key)
912a42faf7dSnicm {
913a42faf7dSnicm 	struct window_tree_modedata	*data = modedata;
914a42faf7dSnicm 	struct window_tree_itemdata	*item = itemdata;
915a42faf7dSnicm 	char				*name;
916a42faf7dSnicm 	struct cmd_find_state		 fs;
917a42faf7dSnicm 
918a42faf7dSnicm 	name = window_tree_get_target(item, &fs);
919a42faf7dSnicm 	if (name != NULL)
920d7af2c28Snicm 		mode_tree_run_command(c, &fs, data->entered, name);
921a42faf7dSnicm 	free(name);
922a42faf7dSnicm }
923a42faf7dSnicm 
924a42faf7dSnicm static enum cmd_retval
925a42faf7dSnicm window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
926a42faf7dSnicm {
927a42faf7dSnicm 	struct window_tree_modedata	*data = modedata;
928a42faf7dSnicm 
929a42faf7dSnicm 	if (!data->dead) {
930a42faf7dSnicm 		mode_tree_build(data->data);
931a42faf7dSnicm 		mode_tree_draw(data->data);
932a42faf7dSnicm 		data->wp->flags |= PANE_REDRAW;
933a42faf7dSnicm 	}
934a42faf7dSnicm 	window_tree_destroy(data);
935a42faf7dSnicm 	return (CMD_RETURN_NORMAL);
936a42faf7dSnicm }
937a42faf7dSnicm 
938a42faf7dSnicm static int
939a42faf7dSnicm window_tree_command_callback(struct client *c, void *modedata, const char *s,
940a42faf7dSnicm     __unused int done)
941a42faf7dSnicm {
942a42faf7dSnicm 	struct window_tree_modedata	*data = modedata;
943a42faf7dSnicm 
944d0f3ba18Snicm 	if (s == NULL || data->dead)
945a42faf7dSnicm 		return (0);
946a42faf7dSnicm 
947a42faf7dSnicm 	data->entered = s;
948d7af2c28Snicm 	mode_tree_each_tagged(data->data, window_tree_command_each, c,
949d7af2c28Snicm 	    KEYC_NONE, 1);
950a42faf7dSnicm 	data->entered = NULL;
951a42faf7dSnicm 
952a42faf7dSnicm 	data->references++;
953a42faf7dSnicm 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
954a42faf7dSnicm 
955a42faf7dSnicm 	return (0);
956a42faf7dSnicm }
957a42faf7dSnicm 
958a42faf7dSnicm static void
959a42faf7dSnicm window_tree_command_free(void *modedata)
960a42faf7dSnicm {
961a42faf7dSnicm 	struct window_tree_modedata	*data = modedata;
962a42faf7dSnicm 
963a42faf7dSnicm 	window_tree_destroy(data);
964a42faf7dSnicm }
965a42faf7dSnicm 
966a42faf7dSnicm static void
967a42faf7dSnicm window_tree_key(struct window_pane *wp, struct client *c,
968a42faf7dSnicm     __unused struct session *s, key_code key, struct mouse_event *m)
969a42faf7dSnicm {
970a42faf7dSnicm 	struct window_tree_modedata	*data = wp->modedata;
971a42faf7dSnicm 	struct window_tree_itemdata	*item;
972d7af2c28Snicm 	char				*name, *prompt;
973a42faf7dSnicm 	struct cmd_find_state		 fs;
974a42faf7dSnicm 	int				 finished;
975a42faf7dSnicm 	u_int				 tagged;
976a42faf7dSnicm 
97705d44586Snicm 	item = mode_tree_get_current(data->data);
978943a08b1Snicm 	finished = mode_tree_key(data->data, c, &key, m);
97905d44586Snicm 	if (item != mode_tree_get_current(data->data))
98005d44586Snicm 		data->offset = 0;
981a42faf7dSnicm 	switch (key) {
98205d44586Snicm 	case '<':
98305d44586Snicm 		data->offset--;
98405d44586Snicm 		break;
98505d44586Snicm 	case '>':
98605d44586Snicm 		data->offset++;
98705d44586Snicm 		break;
988a42faf7dSnicm 	case ':':
989a42faf7dSnicm 		tagged = mode_tree_count_tagged(data->data);
990a42faf7dSnicm 		if (tagged != 0)
991a42faf7dSnicm 			xasprintf(&prompt, "(%u tagged) ", tagged);
992a42faf7dSnicm 		else
993a42faf7dSnicm 			xasprintf(&prompt, "(current) ");
994a42faf7dSnicm 		data->references++;
995a42faf7dSnicm 		status_prompt_set(c, prompt, "", window_tree_command_callback,
996a42faf7dSnicm 		    window_tree_command_free, data, PROMPT_NOFORMAT);
997a42faf7dSnicm 		free(prompt);
998a42faf7dSnicm 		break;
999a42faf7dSnicm 	case '\r':
1000a42faf7dSnicm 		item = mode_tree_get_current(data->data);
1001a42faf7dSnicm 		name = window_tree_get_target(item, &fs);
1002a42faf7dSnicm 		if (name != NULL)
1003d7af2c28Snicm 			mode_tree_run_command(c, NULL, data->command, name);
1004d7af2c28Snicm 		finished = 1;
1005a42faf7dSnicm 		free(name);
1006d7af2c28Snicm 		break;
1007a42faf7dSnicm 	}
1008a42faf7dSnicm 	if (finished)
1009a42faf7dSnicm 		window_pane_reset_mode(wp);
1010a42faf7dSnicm 	else {
1011a42faf7dSnicm 		mode_tree_draw(data->data);
1012a42faf7dSnicm 		wp->flags |= PANE_REDRAW;
1013a42faf7dSnicm 	}
1014a42faf7dSnicm }
1015