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