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