Home | History | Annotate | Line # | Download | only in dist
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott (at) 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_update(struct window_mode_entry *);
     33 static void		 window_tree_key(struct window_mode_entry *,
     34 			     struct client *, struct session *,
     35 			     struct winlink *, key_code, struct mouse_event *);
     36 
     37 #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -Zt '%%'"
     38 
     39 #define WINDOW_TREE_DEFAULT_FORMAT \
     40 	"#{?pane_format," \
     41 		"#{?pane_marked,#[reverse],}" \
     42 		"#{pane_current_command}#{?pane_active,*,}#{?pane_marked,M,}" \
     43 		"#{?#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}},: \"#{pane_title}\",}" \
     44 	",window_format," \
     45 		"#{?window_marked_flag,#[reverse],}" \
     46 		"#{window_name}#{window_flags}" \
     47 		"#{?#{&&:#{==:#{window_panes},1},#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}}},: \"#{pane_title}\",}" \
     48 	"," \
     49 		"#{session_windows} windows" \
     50 		"#{?session_grouped, " \
     51 			"(group #{session_group}: " \
     52 			"#{session_group_list})," \
     53 		"}" \
     54 		"#{?session_attached, (attached),}" \
     55 	"}"
     56 
     57 #define WINDOW_TREE_DEFAULT_KEY_FORMAT \
     58 	"#{?#{e|<:#{line},10}," \
     59 		"#{line}" \
     60 	",#{e|<:#{line},36},"	\
     61 	        "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
     62 	"}"
     63 
     64 static const struct menu_item window_tree_menu_items[] = {
     65 	{ "Select", '\r', NULL },
     66 	{ "Expand", KEYC_RIGHT, NULL },
     67 	{ "Mark", 'm', NULL },
     68 	{ "", KEYC_NONE, NULL },
     69 	{ "Tag", 't', NULL },
     70 	{ "Tag All", '\024', NULL },
     71 	{ "Tag None", 'T', NULL },
     72 	{ "", KEYC_NONE, NULL },
     73 	{ "Kill", 'x', NULL },
     74 	{ "Kill Tagged", 'X', NULL },
     75 	{ "", KEYC_NONE, NULL },
     76 	{ "Cancel", 'q', NULL },
     77 
     78 	{ NULL, KEYC_NONE, NULL }
     79 };
     80 
     81 const struct window_mode window_tree_mode = {
     82 	.name = "tree-mode",
     83 	.default_format = WINDOW_TREE_DEFAULT_FORMAT,
     84 
     85 	.init = window_tree_init,
     86 	.free = window_tree_free,
     87 	.resize = window_tree_resize,
     88 	.update = window_tree_update,
     89 	.key = window_tree_key,
     90 };
     91 
     92 enum window_tree_sort_type {
     93 	WINDOW_TREE_BY_INDEX,
     94 	WINDOW_TREE_BY_NAME,
     95 	WINDOW_TREE_BY_TIME,
     96 };
     97 static const char *window_tree_sort_list[] = {
     98 	"index",
     99 	"name",
    100 	"time"
    101 };
    102 static struct mode_tree_sort_criteria *window_tree_sort;
    103 
    104 enum window_tree_type {
    105 	WINDOW_TREE_NONE,
    106 	WINDOW_TREE_SESSION,
    107 	WINDOW_TREE_WINDOW,
    108 	WINDOW_TREE_PANE,
    109 };
    110 
    111 struct window_tree_itemdata {
    112 	enum window_tree_type	type;
    113 	int			session;
    114 	int			winlink;
    115 	int			pane;
    116 };
    117 
    118 struct window_tree_modedata {
    119 	struct window_pane		 *wp;
    120 	int				  dead;
    121 	int				  references;
    122 
    123 	struct mode_tree_data		 *data;
    124 	char				 *format;
    125 	char				 *key_format;
    126 	char				 *command;
    127 	int				  squash_groups;
    128 	int				  prompt_flags;
    129 
    130 	struct window_tree_itemdata	**item_list;
    131 	u_int				  item_size;
    132 
    133 	const char			 *entered;
    134 
    135 	struct cmd_find_state		  fs;
    136 	enum window_tree_type		  type;
    137 
    138 	int				  offset;
    139 
    140 	int				  left;
    141 	int				  right;
    142 	u_int				  start;
    143 	u_int				  end;
    144 	u_int				  each;
    145 };
    146 
    147 static void
    148 window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
    149     struct winlink **wlp, struct window_pane **wp)
    150 {
    151 	*wp = NULL;
    152 	*wlp = NULL;
    153 	*sp = session_find_by_id(item->session);
    154 	if (*sp == NULL)
    155 		return;
    156 	if (item->type == WINDOW_TREE_SESSION) {
    157 		*wlp = (*sp)->curw;
    158 		*wp = (*wlp)->window->active;
    159 		return;
    160 	}
    161 
    162 	*wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
    163 	if (*wlp == NULL) {
    164 		*sp = NULL;
    165 		return;
    166 	}
    167 	if (item->type == WINDOW_TREE_WINDOW) {
    168 		*wp = (*wlp)->window->active;
    169 		return;
    170 	}
    171 
    172 	*wp = window_pane_find_by_id(item->pane);
    173 	if (!window_has_pane((*wlp)->window, *wp))
    174 		*wp = NULL;
    175 	if (*wp == NULL) {
    176 		*sp = NULL;
    177 		*wlp = NULL;
    178 		return;
    179 	}
    180 }
    181 
    182 static struct window_tree_itemdata *
    183 window_tree_add_item(struct window_tree_modedata *data)
    184 {
    185 	struct window_tree_itemdata	*item;
    186 
    187 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
    188 	    sizeof *data->item_list);
    189 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
    190 	return (item);
    191 }
    192 
    193 static void
    194 window_tree_free_item(struct window_tree_itemdata *item)
    195 {
    196 	free(item);
    197 }
    198 
    199 static int
    200 window_tree_cmp_session(const void *a0, const void *b0)
    201 {
    202 	const struct session *const	*a = a0;
    203 	const struct session *const	*b = b0;
    204 	const struct session		*sa = *a;
    205 	const struct session		*sb = *b;
    206 	int				 result = 0;
    207 
    208 	switch (window_tree_sort->field) {
    209 	case WINDOW_TREE_BY_INDEX:
    210 		result = sa->id - sb->id;
    211 		break;
    212 	case WINDOW_TREE_BY_TIME:
    213 		if (timercmp(&sa->activity_time, &sb->activity_time, >)) {
    214 			result = -1;
    215 			break;
    216 		}
    217 		if (timercmp(&sa->activity_time, &sb->activity_time, <)) {
    218 			result = 1;
    219 			break;
    220 		}
    221 		/* FALLTHROUGH */
    222 	case WINDOW_TREE_BY_NAME:
    223 		result = strcmp(sa->name, sb->name);
    224 		break;
    225 	}
    226 
    227 	if (window_tree_sort->reversed)
    228 		result = -result;
    229 	return (result);
    230 }
    231 
    232 static int
    233 window_tree_cmp_window(const void *a0, const void *b0)
    234 {
    235 	const struct winlink *const	*a = a0;
    236 	const struct winlink *const	*b = b0;
    237 	const struct winlink		*wla = *a;
    238 	const struct winlink		*wlb = *b;
    239 	struct window			*wa = wla->window;
    240 	struct window			*wb = wlb->window;
    241 	int				 result = 0;
    242 
    243 	switch (window_tree_sort->field) {
    244 	case WINDOW_TREE_BY_INDEX:
    245 		result = wla->idx - wlb->idx;
    246 		break;
    247 	case WINDOW_TREE_BY_TIME:
    248 		if (timercmp(&wa->activity_time, &wb->activity_time, >)) {
    249 			result = -1;
    250 			break;
    251 		}
    252 		if (timercmp(&wa->activity_time, &wb->activity_time, <)) {
    253 			result = 1;
    254 			break;
    255 		}
    256 		/* FALLTHROUGH */
    257 	case WINDOW_TREE_BY_NAME:
    258 		result = strcmp(wa->name, wb->name);
    259 		break;
    260 	}
    261 
    262 	if (window_tree_sort->reversed)
    263 		result = -result;
    264 	return (result);
    265 }
    266 
    267 static int
    268 window_tree_cmp_pane(const void *a0, const void *b0)
    269 {
    270 	struct window_pane	**a = (struct window_pane **)__UNCONST(a0);
    271 	struct window_pane	**b = (struct window_pane **)__UNCONST(b0);
    272 	int			  result;
    273 	u_int			  ai, bi;
    274 
    275 	if (window_tree_sort->field == WINDOW_TREE_BY_TIME)
    276 		result = (*a)->active_point - (*b)->active_point;
    277 	else {
    278 		/*
    279 		 * Panes don't have names, so use number order for any other
    280 		 * sort field.
    281 		 */
    282 		window_pane_index(*a, &ai);
    283 		window_pane_index(*b, &bi);
    284 		result = ai - bi;
    285 	}
    286 	if (window_tree_sort->reversed)
    287 		result = -result;
    288 	return (result);
    289 }
    290 
    291 static void
    292 window_tree_build_pane(struct session *s, struct winlink *wl,
    293     struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
    294 {
    295 	struct window_tree_modedata	*data = modedata;
    296 	struct window_tree_itemdata	*item;
    297 	struct mode_tree_item		*mti;
    298 	char				*name, *text;
    299 	u_int				 idx;
    300 	struct format_tree		*ft;
    301 
    302 	window_pane_index(wp, &idx);
    303 
    304 	item = window_tree_add_item(data);
    305 	item->type = WINDOW_TREE_PANE;
    306 	item->session = s->id;
    307 	item->winlink = wl->idx;
    308 	item->pane = wp->id;
    309 
    310 	ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 0);
    311 	format_defaults(ft, NULL, s, wl, wp);
    312 	text = format_expand(ft, data->format);
    313 	xasprintf(&name, "%u", idx);
    314 	format_free(ft);
    315 
    316 	mti = mode_tree_add(data->data, parent, item, (uintptr_t)wp, name, text,
    317 	    -1);
    318 	free(text);
    319 	free(name);
    320 	mode_tree_align(mti, 1);
    321 }
    322 
    323 static int
    324 window_tree_filter_pane(struct session *s, struct winlink *wl,
    325     struct window_pane *wp, const char *filter)
    326 {
    327 	char	*cp;
    328 	int	 result;
    329 
    330 	if (filter == NULL)
    331 		return (1);
    332 
    333 	cp = format_single(NULL, filter, NULL, s, wl, wp);
    334 	result = format_true(cp);
    335 	free(cp);
    336 
    337 	return (result);
    338 }
    339 
    340 static int
    341 window_tree_build_window(struct session *s, struct winlink *wl,
    342     void *modedata, struct mode_tree_sort_criteria *sort_crit,
    343     struct mode_tree_item *parent, const char *filter)
    344 {
    345 	struct window_tree_modedata	*data = modedata;
    346 	struct window_tree_itemdata	*item;
    347 	struct mode_tree_item		*mti;
    348 	char				*name, *text;
    349 	struct window_pane		*wp, **l;
    350 	u_int				 n, i;
    351 	int				 expanded;
    352 	struct format_tree		*ft;
    353 
    354 	item = window_tree_add_item(data);
    355 	item->type = WINDOW_TREE_WINDOW;
    356 	item->session = s->id;
    357 	item->winlink = wl->idx;
    358 	item->pane = -1;
    359 
    360 	ft = format_create(NULL, NULL, FORMAT_PANE|wl->window->active->id, 0);
    361 	format_defaults(ft, NULL, s, wl, NULL);
    362 	text = format_expand(ft, data->format);
    363 	xasprintf(&name, "%u", wl->idx);
    364 	format_free(ft);
    365 
    366 	if (data->type == WINDOW_TREE_SESSION ||
    367 	    data->type == WINDOW_TREE_WINDOW)
    368 		expanded = 0;
    369 	else
    370 		expanded = 1;
    371 	mti = mode_tree_add(data->data, parent, item, (uintptr_t)wl, name, text,
    372 	    expanded);
    373 	free(text);
    374 	free(name);
    375 	mode_tree_align(mti, 1);
    376 
    377 	if ((wp = TAILQ_FIRST(&wl->window->panes)) == NULL)
    378 		goto empty;
    379 	if (TAILQ_NEXT(wp, entry) == NULL) {
    380 		if (!window_tree_filter_pane(s, wl, wp, filter))
    381 			goto empty;
    382 	}
    383 
    384 	l = NULL;
    385 	n = 0;
    386 
    387 	TAILQ_FOREACH(wp, &wl->window->panes, entry) {
    388 		if (!window_tree_filter_pane(s, wl, wp, filter))
    389 			continue;
    390 		l = xreallocarray(l, n + 1, sizeof *l);
    391 		l[n++] = wp;
    392 	}
    393 	if (n == 0)
    394 		goto empty;
    395 
    396 	window_tree_sort = sort_crit;
    397 	qsort(l, n, sizeof *l, window_tree_cmp_pane);
    398 
    399 	for (i = 0; i < n; i++)
    400 		window_tree_build_pane(s, wl, l[i], modedata, mti);
    401 	free(l);
    402 	return (1);
    403 
    404 empty:
    405 	window_tree_free_item(item);
    406 	data->item_size--;
    407 	mode_tree_remove(data->data, mti);
    408 	return (0);
    409 }
    410 
    411 static void
    412 window_tree_build_session(struct session *s, void *modedata,
    413     struct mode_tree_sort_criteria *sort_crit, const char *filter)
    414 {
    415 	struct window_tree_modedata	*data = modedata;
    416 	struct window_tree_itemdata	*item;
    417 	struct mode_tree_item		*mti;
    418 	char				*text;
    419 	struct winlink			*wl = s->curw, **l;
    420 	u_int				 n, i, empty;
    421 	int				 expanded;
    422 	struct format_tree		*ft;
    423 
    424 	item = window_tree_add_item(data);
    425 	item->type = WINDOW_TREE_SESSION;
    426 	item->session = s->id;
    427 	item->winlink = -1;
    428 	item->pane = -1;
    429 
    430 	ft = format_create(NULL, NULL, FORMAT_PANE|wl->window->active->id, 0);
    431 	format_defaults(ft, NULL, s, NULL, NULL);
    432 	text = format_expand(ft, data->format);
    433 	format_free(ft);
    434 
    435 	if (data->type == WINDOW_TREE_SESSION)
    436 		expanded = 0;
    437 	else
    438 		expanded = 1;
    439 	mti = mode_tree_add(data->data, NULL, item, (uintptr_t)s, s->name, text,
    440 	    expanded);
    441 	free(text);
    442 
    443 	l = NULL;
    444 	n = 0;
    445 	RB_FOREACH(wl, winlinks, &s->windows) {
    446 		l = xreallocarray(l, n + 1, sizeof *l);
    447 		l[n++] = wl;
    448 	}
    449 	window_tree_sort = sort_crit;
    450 	qsort(l, n, sizeof *l, window_tree_cmp_window);
    451 
    452 	empty = 0;
    453 	for (i = 0; i < n; i++) {
    454 		if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti,
    455 		    filter))
    456 			empty++;
    457 	}
    458 	if (empty == n) {
    459 		window_tree_free_item(item);
    460 		data->item_size--;
    461 		mode_tree_remove(data->data, mti);
    462 	}
    463 	free(l);
    464 }
    465 
    466 static void
    467 window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
    468     uint64_t *tag, const char *filter)
    469 {
    470 	struct window_tree_modedata	*data = modedata;
    471 	struct session			*s, **l;
    472 	struct session_group		*sg, *current;
    473 	u_int				 n, i;
    474 
    475 	current = session_group_contains(data->fs.s);
    476 
    477 	for (i = 0; i < data->item_size; i++)
    478 		window_tree_free_item(data->item_list[i]);
    479 	free(data->item_list);
    480 	data->item_list = NULL;
    481 	data->item_size = 0;
    482 
    483 	l = NULL;
    484 	n = 0;
    485 	RB_FOREACH(s, sessions, &sessions) {
    486 		if (data->squash_groups &&
    487 		    (sg = session_group_contains(s)) != NULL) {
    488 			if ((sg == current && s != data->fs.s) ||
    489 			    (sg != current && s != TAILQ_FIRST(&sg->sessions)))
    490 				continue;
    491 		}
    492 		l = xreallocarray(l, n + 1, sizeof *l);
    493 		l[n++] = s;
    494 	}
    495 	window_tree_sort = sort_crit;
    496 	qsort(l, n, sizeof *l, window_tree_cmp_session);
    497 
    498 	for (i = 0; i < n; i++)
    499 		window_tree_build_session(l[i], modedata, sort_crit, filter);
    500 	free(l);
    501 
    502 	switch (data->type) {
    503 	case WINDOW_TREE_NONE:
    504 		break;
    505 	case WINDOW_TREE_SESSION:
    506 		*tag = (uintptr_t)data->fs.s;
    507 		break;
    508 	case WINDOW_TREE_WINDOW:
    509 		*tag = (uintptr_t)data->fs.wl;
    510 		break;
    511 	case WINDOW_TREE_PANE:
    512 		if (window_count_panes(data->fs.wl->window) == 1)
    513 			*tag = (uintptr_t)data->fs.wl;
    514 		else
    515 			*tag = (uintptr_t)data->fs.wp;
    516 		break;
    517 	}
    518 }
    519 
    520 static void
    521 window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
    522     u_int sx, u_int sy, const struct grid_cell *gc, const char *label)
    523 {
    524 	size_t	 len;
    525 	u_int	 ox, oy;
    526 
    527 	len = strlen(label);
    528 	if (sx == 0 || sy == 1 || len > sx)
    529 		return;
    530 	ox = (sx - len + 1) / 2;
    531 	oy = (sy + 1) / 2;
    532 
    533 	if (ox > 1 && ox + len < sx - 1 && sy >= 3) {
    534 		screen_write_cursormove(ctx, px + ox - 1, py + oy - 1, 0);
    535 		screen_write_box(ctx, len + 2, 3, BOX_LINES_DEFAULT, NULL,
    536 		    NULL);
    537 	}
    538 	screen_write_cursormove(ctx, px + ox, py + oy, 0);
    539 	screen_write_puts(ctx, gc, "%s", label);
    540 }
    541 
    542 static void
    543 window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
    544     struct screen_write_ctx *ctx, u_int sx, u_int sy)
    545 {
    546 	struct options		*oo = s->options;
    547 	struct winlink		*wl;
    548 	struct window		*w;
    549 	u_int			 cx = ctx->s->cx, cy = ctx->s->cy;
    550 	u_int			 loop, total, visible, each, width, offset;
    551 	u_int			 current, start, end, remaining, i;
    552 	struct grid_cell	 gc;
    553 	int			 colour, active_colour, left, right;
    554 	char			*label;
    555 
    556 	total = winlink_count(&s->windows);
    557 
    558 	memcpy(&gc, &grid_default_cell, sizeof gc);
    559 	colour = options_get_number(oo, "display-panes-colour");
    560 	active_colour = options_get_number(oo, "display-panes-active-colour");
    561 
    562 	if (sx / total < 24) {
    563 		visible = sx / 24;
    564 		if (visible == 0)
    565 			visible = 1;
    566 	} else
    567 		visible = total;
    568 
    569 	current = 0;
    570 	RB_FOREACH(wl, winlinks, &s->windows) {
    571 		if (wl == s->curw)
    572 			break;
    573 		current++;
    574 	}
    575 
    576 	if (current < visible) {
    577 		start = 0;
    578 		end = visible;
    579 	} else if (current >= total - visible) {
    580 		start = total - visible;
    581 		end = total;
    582 	} else {
    583 		start = current - (visible / 2);
    584 		end = start + visible;
    585 	}
    586 
    587 	if (data->offset < -(int)start)
    588 		data->offset = -(int)start;
    589 	if (data->offset > (int)(total - end))
    590 		data->offset = (int)(total - end);
    591 	start += data->offset;
    592 	end += data->offset;
    593 
    594 	left = (start != 0);
    595 	right = (end != total);
    596 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
    597 		left = right = 0;
    598 	if (left && right) {
    599 		each = (sx - 6) / visible;
    600 		remaining = (sx - 6) - (visible * each);
    601 	} else if (left || right) {
    602 		each = (sx - 3) / visible;
    603 		remaining = (sx - 3) - (visible * each);
    604 	} else {
    605 		each = sx / visible;
    606 		remaining = sx - (visible * each);
    607 	}
    608 	if (each == 0)
    609 		return;
    610 
    611 	if (left) {
    612 		data->left = cx + 2;
    613 		screen_write_cursormove(ctx, cx + 2, cy, 0);
    614 		screen_write_vline(ctx, sy, 0, 0);
    615 		screen_write_cursormove(ctx, cx, cy + sy / 2, 0);
    616 		screen_write_puts(ctx, &grid_default_cell, "<");
    617 	} else
    618 		data->left = -1;
    619 	if (right) {
    620 		data->right = cx + sx - 3;
    621 		screen_write_cursormove(ctx, cx + sx - 3, cy, 0);
    622 		screen_write_vline(ctx, sy, 0, 0);
    623 		screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0);
    624 		screen_write_puts(ctx, &grid_default_cell, ">");
    625 	} else
    626 		data->right = -1;
    627 
    628 	data->start = start;
    629 	data->end = end;
    630 	data->each = each;
    631 
    632 	i = loop = 0;
    633 	RB_FOREACH(wl, winlinks, &s->windows) {
    634 		if (loop == end)
    635 			break;
    636 		if (loop < start) {
    637 			loop++;
    638 			continue;
    639 		}
    640 		w = wl->window;
    641 
    642 		if (wl == s->curw)
    643 			gc.fg = active_colour;
    644 		else
    645 			gc.fg = colour;
    646 
    647 		if (left)
    648 			offset = 3 + (i * each);
    649 		else
    650 			offset = (i * each);
    651 		if (loop == end - 1)
    652 			width = each + remaining;
    653 		else
    654 			width = each - 1;
    655 
    656 		screen_write_cursormove(ctx, cx + offset, cy, 0);
    657 		screen_write_preview(ctx, &w->active->base, width, sy);
    658 
    659 		xasprintf(&label, " %u:%s ", wl->idx, w->name);
    660 		if (strlen(label) > width) {
    661 			free(label);
    662 			xasprintf(&label, " %u ", wl->idx);
    663 		}
    664 		window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc,
    665 		    label);
    666 		free(label);
    667 
    668 		if (loop != end - 1) {
    669 			screen_write_cursormove(ctx, cx + offset + width, cy, 0);
    670 			screen_write_vline(ctx, sy, 0, 0);
    671 		}
    672 		loop++;
    673 
    674 		i++;
    675 	}
    676 }
    677 
    678 static void
    679 window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
    680     struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy)
    681 {
    682 	struct options		*oo = s->options;
    683 	struct window_pane	*wp;
    684 	u_int			 cx = ctx->s->cx, cy = ctx->s->cy;
    685 	u_int			 loop, total, visible, each, width, offset;
    686 	u_int			 current, start, end, remaining, i, pane_idx;
    687 	struct grid_cell	 gc;
    688 	int			 colour, active_colour, left, right;
    689 	char			*label;
    690 
    691 	total = window_count_panes(w);
    692 
    693 	memcpy(&gc, &grid_default_cell, sizeof gc);
    694 	colour = options_get_number(oo, "display-panes-colour");
    695 	active_colour = options_get_number(oo, "display-panes-active-colour");
    696 
    697 	if (sx / total < 24) {
    698 		visible = sx / 24;
    699 		if (visible == 0)
    700 			visible = 1;
    701 	} else
    702 		visible = total;
    703 
    704 	current = 0;
    705 	TAILQ_FOREACH(wp, &w->panes, entry) {
    706 		if (wp == w->active)
    707 			break;
    708 		current++;
    709 	}
    710 
    711 	if (current < visible) {
    712 		start = 0;
    713 		end = visible;
    714 	} else if (current >= total - visible) {
    715 		start = total - visible;
    716 		end = total;
    717 	} else {
    718 		start = current - (visible / 2);
    719 		end = start + visible;
    720 	}
    721 
    722 	if (data->offset < -(int)start)
    723 		data->offset = -(int)start;
    724 	if (data->offset > (int)(total - end))
    725 		data->offset = (int)(total - end);
    726 	start += data->offset;
    727 	end += data->offset;
    728 
    729 	left = (start != 0);
    730 	right = (end != total);
    731 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
    732 		left = right = 0;
    733 	if (left && right) {
    734 		each = (sx - 6) / visible;
    735 		remaining = (sx - 6) - (visible * each);
    736 	} else if (left || right) {
    737 		each = (sx - 3) / visible;
    738 		remaining = (sx - 3) - (visible * each);
    739 	} else {
    740 		each = sx / visible;
    741 		remaining = sx - (visible * each);
    742 	}
    743 	if (each == 0)
    744 		return;
    745 
    746 	if (left) {
    747 		data->left = cx + 2;
    748 		screen_write_cursormove(ctx, cx + 2, cy, 0);
    749 		screen_write_vline(ctx, sy, 0, 0);
    750 		screen_write_cursormove(ctx, cx, cy + sy / 2, 0);
    751 		screen_write_puts(ctx, &grid_default_cell, "<");
    752 	} else
    753 		data->left = -1;
    754 	if (right) {
    755 		data->right = cx + sx - 3;
    756 		screen_write_cursormove(ctx, cx + sx - 3, cy, 0);
    757 		screen_write_vline(ctx, sy, 0, 0);
    758 		screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0);
    759 		screen_write_puts(ctx, &grid_default_cell, ">");
    760 	} else
    761 		data->right = -1;
    762 
    763 	data->start = start;
    764 	data->end = end;
    765 	data->each = each;
    766 
    767 	i = loop = 0;
    768 	TAILQ_FOREACH(wp, &w->panes, entry) {
    769 		if (loop == end)
    770 			break;
    771 		if (loop < start) {
    772 			loop++;
    773 			continue;
    774 		}
    775 
    776 		if (wp == w->active)
    777 			gc.fg = active_colour;
    778 		else
    779 			gc.fg = colour;
    780 
    781 		if (left)
    782 			offset = 3 + (i * each);
    783 		else
    784 			offset = (i * each);
    785 		if (loop == end - 1)
    786 			width = each + remaining;
    787 		else
    788 			width = each - 1;
    789 
    790 		screen_write_cursormove(ctx, cx + offset, cy, 0);
    791 		screen_write_preview(ctx, &wp->base, width, sy);
    792 
    793 		if (window_pane_index(wp, &pane_idx) != 0)
    794 			pane_idx = loop;
    795 		xasprintf(&label, " %u ", pane_idx);
    796 		window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc,
    797 		    label);
    798 		free(label);
    799 
    800 		if (loop != end - 1) {
    801 			screen_write_cursormove(ctx, cx + offset + width, cy, 0);
    802 			screen_write_vline(ctx, sy, 0, 0);
    803 		}
    804 		loop++;
    805 
    806 		i++;
    807 	}
    808 }
    809 
    810 static void
    811 window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx,
    812     u_int sx, u_int sy)
    813 {
    814 	struct window_tree_itemdata	*item = itemdata;
    815 	struct session			*sp;
    816 	struct winlink			*wlp;
    817 	struct window_pane		*wp;
    818 
    819 	window_tree_pull_item(item, &sp, &wlp, &wp);
    820 	if (wp == NULL)
    821 		return;
    822 
    823 	switch (item->type) {
    824 	case WINDOW_TREE_NONE:
    825 		break;
    826 	case WINDOW_TREE_SESSION:
    827 		window_tree_draw_session(modedata, sp, ctx, sx, sy);
    828 		break;
    829 	case WINDOW_TREE_WINDOW:
    830 		window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy);
    831 		break;
    832 	case WINDOW_TREE_PANE:
    833 		screen_write_preview(ctx, &wp->base, sx, sy);
    834 		break;
    835 	}
    836 }
    837 
    838 static int
    839 window_tree_search(__unused void *modedata, void *itemdata, const char *ss,
    840     int icase)
    841 {
    842 	struct window_tree_itemdata	*item = itemdata;
    843 	struct session			*s;
    844 	struct winlink			*wl;
    845 	struct window_pane		*wp;
    846 	char				*cmd;
    847 	int				 retval;
    848 
    849 	window_tree_pull_item(item, &s, &wl, &wp);
    850 
    851 	switch (item->type) {
    852 	case WINDOW_TREE_NONE:
    853 		return (0);
    854 	case WINDOW_TREE_SESSION:
    855 		if (s == NULL)
    856 			return (0);
    857 		if (icase)
    858 			return (strcasestr(s->name, ss) != NULL);
    859  		return (strstr(s->name, ss) != NULL);
    860 	case WINDOW_TREE_WINDOW:
    861 		if (s == NULL || wl == NULL)
    862 			return (0);
    863 		if (icase)
    864 			return (strcasestr(wl->window->name, ss) != NULL);
    865 		return (strstr(wl->window->name, ss) != NULL);
    866 	case WINDOW_TREE_PANE:
    867 		if (s == NULL || wl == NULL || wp == NULL)
    868 			break;
    869 		cmd = osdep_get_name(wp->fd, wp->tty);
    870 		if (cmd == NULL || *cmd == '\0') {
    871 			free(cmd);
    872 			return (0);
    873 		}
    874 		if (icase)
    875 			retval = (strcasestr(cmd, ss) != NULL);
    876 		else
    877 			retval = (strstr(cmd, ss) != NULL);
    878 		free(cmd);
    879 		return (retval);
    880 	}
    881 	return (0);
    882 }
    883 
    884 static void
    885 window_tree_menu(void *modedata, struct client *c, key_code key)
    886 {
    887 	struct window_tree_modedata	*data = modedata;
    888 	struct window_pane		*wp = data->wp;
    889 	struct window_mode_entry	*wme;
    890 
    891 	wme = TAILQ_FIRST(&wp->modes);
    892 	if (wme == NULL || wme->data != modedata)
    893 		return;
    894 	window_tree_key(wme, c, NULL, NULL, key, NULL);
    895 }
    896 
    897 static key_code
    898 window_tree_get_key(void *modedata, void *itemdata, u_int line)
    899 {
    900 	struct window_tree_modedata	*data = modedata;
    901 	struct window_tree_itemdata	*item = itemdata;
    902 	struct format_tree		*ft;
    903 	struct session			*s;
    904 	struct winlink			*wl;
    905 	struct window_pane		*wp;
    906 	char				*expanded;
    907 	key_code			 key;
    908 
    909 	ft = format_create(NULL, NULL, FORMAT_NONE, 0);
    910 	window_tree_pull_item(item, &s, &wl, &wp);
    911 	if (item->type == WINDOW_TREE_SESSION)
    912 		format_defaults(ft, NULL, s, NULL, NULL);
    913 	else if (item->type == WINDOW_TREE_WINDOW)
    914 		format_defaults(ft, NULL, s, wl, NULL);
    915 	else
    916 		format_defaults(ft, NULL, s, wl, wp);
    917 	format_add(ft, "line", "%u", line);
    918 
    919 	expanded = format_expand(ft, data->key_format);
    920 	key = key_string_lookup_string(expanded);
    921 	free(expanded);
    922 	format_free(ft);
    923 	return (key);
    924 }
    925 
    926 static int
    927 window_tree_swap(void *cur_itemdata, void *other_itemdata)
    928 {
    929 	struct window_tree_itemdata	*cur = cur_itemdata;
    930 	struct window_tree_itemdata	*other = other_itemdata;
    931 	struct session			*cur_session, *other_session;
    932 	struct winlink			*cur_winlink, *other_winlink;
    933 	struct window			*cur_window, *other_window;
    934 	struct window_pane		*cur_pane, *other_pane;
    935 
    936 	if (cur->type != other->type)
    937 		return (0);
    938 	if (cur->type != WINDOW_TREE_WINDOW)
    939 		return (0);
    940 
    941 	window_tree_pull_item(cur, &cur_session, &cur_winlink, &cur_pane);
    942 	window_tree_pull_item(other, &other_session, &other_winlink,
    943 	    &other_pane);
    944 
    945 	if (cur_session != other_session)
    946 		return (0);
    947 
    948 	if (window_tree_sort->field != WINDOW_TREE_BY_INDEX &&
    949 	    window_tree_cmp_window(&cur_winlink, &other_winlink) != 0) {
    950 		/*
    951 		 * Swapping indexes would not swap positions in the tree, so
    952 		 * prevent swapping to avoid confusing the user.
    953 		 */
    954 		return (0);
    955 	}
    956 
    957 	other_window = other_winlink->window;
    958 	TAILQ_REMOVE(&other_window->winlinks, other_winlink, wentry);
    959 	cur_window = cur_winlink->window;
    960 	TAILQ_REMOVE(&cur_window->winlinks, cur_winlink, wentry);
    961 
    962 	other_winlink->window = cur_window;
    963 	TAILQ_INSERT_TAIL(&cur_window->winlinks, other_winlink, wentry);
    964 	cur_winlink->window = other_window;
    965 	TAILQ_INSERT_TAIL(&other_window->winlinks, cur_winlink, wentry);
    966 
    967 	if (cur_session->curw == cur_winlink)
    968 		session_set_current(cur_session, other_winlink);
    969 	else if (cur_session->curw == other_winlink)
    970 		session_set_current(cur_session, cur_winlink);
    971 	session_group_synchronize_from(cur_session);
    972 	server_redraw_session_group(cur_session);
    973 	recalculate_sizes();
    974 
    975 	return (1);
    976 }
    977 
    978 static struct screen *
    979 window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
    980     struct args *args)
    981 {
    982 	struct window_pane		*wp = wme->wp;
    983 	struct window_tree_modedata	*data;
    984 	struct screen			*s;
    985 
    986 	wme->data = data = xcalloc(1, sizeof *data);
    987 	data->wp = wp;
    988 	data->references = 1;
    989 
    990 	if (args_has(args, 's'))
    991 		data->type = WINDOW_TREE_SESSION;
    992 	else if (args_has(args, 'w'))
    993 		data->type = WINDOW_TREE_WINDOW;
    994 	else
    995 		data->type = WINDOW_TREE_PANE;
    996 	memcpy(&data->fs, fs, sizeof data->fs);
    997 
    998 	if (args == NULL || !args_has(args, 'F'))
    999 		data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
   1000 	else
   1001 		data->format = xstrdup(args_get(args, 'F'));
   1002 	if (args == NULL || !args_has(args, 'K'))
   1003 		data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT);
   1004 	else
   1005 		data->key_format = xstrdup(args_get(args, 'K'));
   1006 	if (args == NULL || args_count(args) == 0)
   1007 		data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
   1008 	else
   1009 		data->command = xstrdup(args_string(args, 0));
   1010 	data->squash_groups = !args_has(args, 'G');
   1011 	if (args_has(args, 'y'))
   1012 		data->prompt_flags = PROMPT_ACCEPT;
   1013 
   1014 	data->data = mode_tree_start(wp, args, window_tree_build,
   1015 	    window_tree_draw, window_tree_search, window_tree_menu, NULL,
   1016 	    window_tree_get_key, window_tree_swap, data, window_tree_menu_items,
   1017 	    window_tree_sort_list, nitems(window_tree_sort_list), &s);
   1018 	mode_tree_zoom(data->data, args);
   1019 
   1020 	mode_tree_build(data->data);
   1021 	mode_tree_draw(data->data);
   1022 
   1023 	data->type = WINDOW_TREE_NONE;
   1024 
   1025 	return (s);
   1026 }
   1027 
   1028 static void
   1029 window_tree_destroy(struct window_tree_modedata *data)
   1030 {
   1031 	u_int	i;
   1032 
   1033 	if (--data->references != 0)
   1034 		return;
   1035 
   1036 	for (i = 0; i < data->item_size; i++)
   1037 		window_tree_free_item(data->item_list[i]);
   1038 	free(data->item_list);
   1039 
   1040 	free(data->format);
   1041 	free(data->key_format);
   1042 	free(data->command);
   1043 
   1044 	free(data);
   1045 }
   1046 
   1047 static void
   1048 window_tree_free(struct window_mode_entry *wme)
   1049 {
   1050 	struct window_tree_modedata *data = wme->data;
   1051 
   1052 	if (data == NULL)
   1053 		return;
   1054 
   1055 	data->dead = 1;
   1056 	mode_tree_free(data->data);
   1057 	window_tree_destroy(data);
   1058 }
   1059 
   1060 static void
   1061 window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
   1062 {
   1063 	struct window_tree_modedata	*data = wme->data;
   1064 
   1065 	mode_tree_resize(data->data, sx, sy);
   1066 }
   1067 
   1068 static void
   1069 window_tree_update(struct window_mode_entry *wme)
   1070 {
   1071 	struct window_tree_modedata	*data = wme->data;
   1072 
   1073 	mode_tree_build(data->data);
   1074 	mode_tree_draw(data->data);
   1075 	data->wp->flags |= PANE_REDRAW;
   1076 }
   1077 
   1078 static char *
   1079 window_tree_get_target(struct window_tree_itemdata *item,
   1080     struct cmd_find_state *fs)
   1081 {
   1082 	struct session		*s;
   1083 	struct winlink		*wl;
   1084 	struct window_pane	*wp;
   1085 	char			*target;
   1086 
   1087 	window_tree_pull_item(item, &s, &wl, &wp);
   1088 
   1089 	target = NULL;
   1090 	switch (item->type) {
   1091 	case WINDOW_TREE_NONE:
   1092 		break;
   1093 	case WINDOW_TREE_SESSION:
   1094 		if (s == NULL)
   1095 			break;
   1096 		xasprintf(&target, "=%s:", s->name);
   1097 		break;
   1098 	case WINDOW_TREE_WINDOW:
   1099 		if (s == NULL || wl == NULL)
   1100 			break;
   1101 		xasprintf(&target, "=%s:%u.", s->name, wl->idx);
   1102 		break;
   1103 	case WINDOW_TREE_PANE:
   1104 		if (s == NULL || wl == NULL || wp == NULL)
   1105 			break;
   1106 		xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
   1107 		break;
   1108 	}
   1109 	if (target == NULL)
   1110 		cmd_find_clear_state(fs, 0);
   1111 	else
   1112 		cmd_find_from_winlink_pane(fs, wl, wp, 0);
   1113 	return (target);
   1114 }
   1115 
   1116 static void
   1117 window_tree_command_each(void *modedata, void *itemdata, struct client *c,
   1118     __unused key_code key)
   1119 {
   1120 	struct window_tree_modedata	*data = modedata;
   1121 	struct window_tree_itemdata	*item = itemdata;
   1122 	char				*name;
   1123 	struct cmd_find_state		 fs;
   1124 
   1125 	name = window_tree_get_target(item, &fs);
   1126 	if (name != NULL)
   1127 		mode_tree_run_command(c, &fs, data->entered, name);
   1128 	free(name);
   1129 }
   1130 
   1131 static enum cmd_retval
   1132 window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
   1133 {
   1134 	struct window_tree_modedata	*data = modedata;
   1135 
   1136 	if (!data->dead) {
   1137 		mode_tree_build(data->data);
   1138 		mode_tree_draw(data->data);
   1139 		data->wp->flags |= PANE_REDRAW;
   1140 	}
   1141 	window_tree_destroy(data);
   1142 	return (CMD_RETURN_NORMAL);
   1143 }
   1144 
   1145 static int
   1146 window_tree_command_callback(struct client *c, void *modedata, const char *s,
   1147     __unused int done)
   1148 {
   1149 	struct window_tree_modedata	*data = modedata;
   1150 
   1151 	if (s == NULL || *s == '\0' || data->dead)
   1152 		return (0);
   1153 
   1154 	data->entered = s;
   1155 	mode_tree_each_tagged(data->data, window_tree_command_each, c,
   1156 	    KEYC_NONE, 1);
   1157 	data->entered = NULL;
   1158 
   1159 	data->references++;
   1160 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
   1161 
   1162 	return (0);
   1163 }
   1164 
   1165 static void
   1166 window_tree_command_free(void *modedata)
   1167 {
   1168 	struct window_tree_modedata	*data = modedata;
   1169 
   1170 	window_tree_destroy(data);
   1171 }
   1172 
   1173 static void
   1174 window_tree_kill_each(__unused void *modedata, void *itemdata,
   1175     __unused struct client *c, __unused key_code key)
   1176 {
   1177 	struct window_tree_itemdata	*item = itemdata;
   1178 	struct session			*s;
   1179 	struct winlink			*wl;
   1180 	struct window_pane		*wp;
   1181 
   1182 	window_tree_pull_item(item, &s, &wl, &wp);
   1183 
   1184 	switch (item->type) {
   1185 	case WINDOW_TREE_NONE:
   1186 		break;
   1187 	case WINDOW_TREE_SESSION:
   1188 		if (s != NULL) {
   1189 			server_destroy_session(s);
   1190 			session_destroy(s, 1, __func__);
   1191 		}
   1192 		break;
   1193 	case WINDOW_TREE_WINDOW:
   1194 		if (wl != NULL)
   1195 			server_kill_window(wl->window, 0);
   1196 		break;
   1197 	case WINDOW_TREE_PANE:
   1198 		if (wp != NULL)
   1199 			server_kill_pane(wp);
   1200 		break;
   1201 	}
   1202 }
   1203 
   1204 static int
   1205 window_tree_kill_current_callback(struct client *c, void *modedata,
   1206     const char *s, __unused int done)
   1207 {
   1208 	struct window_tree_modedata	*data = modedata;
   1209 	struct mode_tree_data		*mtd = data->data;
   1210 
   1211 	if (s == NULL || *s == '\0' || data->dead)
   1212 		return (0);
   1213 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
   1214 		return (0);
   1215 
   1216 	window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE);
   1217 	server_renumber_all();
   1218 
   1219 	data->references++;
   1220 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
   1221 
   1222 	return (0);
   1223 }
   1224 
   1225 static int
   1226 window_tree_kill_tagged_callback(struct client *c, void *modedata,
   1227     const char *s, __unused int done)
   1228 {
   1229 	struct window_tree_modedata	*data = modedata;
   1230 	struct mode_tree_data		*mtd = data->data;
   1231 
   1232 	if (s == NULL || *s == '\0' || data->dead)
   1233 		return (0);
   1234 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
   1235 		return (0);
   1236 
   1237 	mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1);
   1238 	server_renumber_all();
   1239 
   1240 	data->references++;
   1241 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
   1242 
   1243 	return (0);
   1244 }
   1245 
   1246 static key_code
   1247 window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x,
   1248     struct window_tree_itemdata *item)
   1249 {
   1250 	struct session		*s;
   1251 	struct winlink		*wl;
   1252 	struct window_pane	*wp;
   1253 	u_int			 loop;
   1254 
   1255 	if (key != KEYC_MOUSEDOWN1_PANE)
   1256 		return (KEYC_NONE);
   1257 
   1258 	if (data->left != -1 && x <= (u_int)data->left)
   1259 		return ('<');
   1260 	if (data->right != -1 && x >= (u_int)data->right)
   1261 		return ('>');
   1262 
   1263 	if (data->left != -1)
   1264 		x -= data->left;
   1265 	else if (x != 0)
   1266 		x--;
   1267 	if (x == 0 || data->end == 0)
   1268 		x = 0;
   1269 	else {
   1270 		x = x / data->each;
   1271 		if (data->start + x >= data->end)
   1272 			x = data->end - 1;
   1273 	}
   1274 
   1275 	window_tree_pull_item(item, &s, &wl, &wp);
   1276 	if (item->type == WINDOW_TREE_SESSION) {
   1277 		if (s == NULL)
   1278 			return (KEYC_NONE);
   1279 		mode_tree_expand_current(data->data);
   1280 		loop = 0;
   1281 		RB_FOREACH(wl, winlinks, &s->windows) {
   1282 			if (loop == data->start + x)
   1283 				break;
   1284 			loop++;
   1285 		}
   1286 		if (wl != NULL)
   1287 			mode_tree_set_current(data->data, (uintptr_t)wl);
   1288 		return ('\r');
   1289 	}
   1290 	if (item->type == WINDOW_TREE_WINDOW) {
   1291 		if (wl == NULL)
   1292 			return (KEYC_NONE);
   1293 		mode_tree_expand_current(data->data);
   1294 		loop = 0;
   1295 		TAILQ_FOREACH(wp, &wl->window->panes, entry) {
   1296 			if (loop == data->start + x)
   1297 				break;
   1298 			loop++;
   1299 		}
   1300 		if (wp != NULL)
   1301 			mode_tree_set_current(data->data, (uintptr_t)wp);
   1302 		return ('\r');
   1303 	}
   1304 	return (KEYC_NONE);
   1305 }
   1306 
   1307 static void
   1308 window_tree_key(struct window_mode_entry *wme, struct client *c,
   1309     __unused struct session *s, __unused struct winlink *wl, key_code key,
   1310     struct mouse_event *m)
   1311 {
   1312 	struct window_pane		*wp = wme->wp;
   1313 	struct window_tree_modedata	*data = wme->data;
   1314 	struct window_tree_itemdata	*item, *new_item;
   1315 	char				*name, *prompt = NULL;
   1316 	struct cmd_find_state		 fs, *fsp = &data->fs;
   1317 	int				 finished;
   1318 	u_int				 tagged, x, y, idx;
   1319 	struct session			*ns;
   1320 	struct winlink			*nwl;
   1321 	struct window_pane		*nwp;
   1322 
   1323 	item = mode_tree_get_current(data->data);
   1324 	finished = mode_tree_key(data->data, c, &key, m, &x, &y);
   1325 
   1326 again:
   1327 	if (item != (new_item = mode_tree_get_current(data->data))) {
   1328 		item = new_item;
   1329 		data->offset = 0;
   1330 	}
   1331 	if (KEYC_IS_MOUSE(key) && m != NULL) {
   1332 		key = window_tree_mouse(data, key, x, item);
   1333 		goto again;
   1334 	}
   1335 
   1336 	switch (key) {
   1337 	case '<':
   1338 		data->offset--;
   1339 		break;
   1340 	case '>':
   1341 		data->offset++;
   1342 		break;
   1343 	case 'H':
   1344 		mode_tree_expand(data->data, (uintptr_t)fsp->s);
   1345 		mode_tree_expand(data->data, (uintptr_t)fsp->wl);
   1346 		if (!mode_tree_set_current(data->data, (uintptr_t)wme->wp))
   1347 			mode_tree_set_current(data->data, (uintptr_t)fsp->wl);
   1348 		break;
   1349 	case 'm':
   1350 		window_tree_pull_item(item, &ns, &nwl, &nwp);
   1351 		server_set_marked(ns, nwl, nwp);
   1352 		mode_tree_build(data->data);
   1353 		break;
   1354 	case 'M':
   1355 		server_clear_marked();
   1356 		mode_tree_build(data->data);
   1357 		break;
   1358 	case 'x':
   1359 		window_tree_pull_item(item, &ns, &nwl, &nwp);
   1360 		switch (item->type) {
   1361 		case WINDOW_TREE_NONE:
   1362 			break;
   1363 		case WINDOW_TREE_SESSION:
   1364 			if (ns == NULL)
   1365 				break;
   1366 			xasprintf(&prompt, "Kill session %s? ", ns->name);
   1367 			break;
   1368 		case WINDOW_TREE_WINDOW:
   1369 			if (nwl == NULL)
   1370 				break;
   1371 			xasprintf(&prompt, "Kill window %u? ", nwl->idx);
   1372 			break;
   1373 		case WINDOW_TREE_PANE:
   1374 			if (nwp == NULL || window_pane_index(nwp, &idx) != 0)
   1375 				break;
   1376 			xasprintf(&prompt, "Kill pane %u? ", idx);
   1377 			break;
   1378 		}
   1379 		if (prompt == NULL)
   1380 			break;
   1381 		data->references++;
   1382 		status_prompt_set(c, NULL, prompt, "",
   1383 		    window_tree_kill_current_callback, window_tree_command_free,
   1384 		    data, PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
   1385 		    PROMPT_TYPE_COMMAND);
   1386 		free(prompt);
   1387 		break;
   1388 	case 'X':
   1389 		tagged = mode_tree_count_tagged(data->data);
   1390 		if (tagged == 0)
   1391 			break;
   1392 		xasprintf(&prompt, "Kill %u tagged? ", tagged);
   1393 		data->references++;
   1394 		status_prompt_set(c, NULL, prompt, "",
   1395 		    window_tree_kill_tagged_callback, window_tree_command_free,
   1396 		    data, PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
   1397 		    PROMPT_TYPE_COMMAND);
   1398 		free(prompt);
   1399 		break;
   1400 	case ':':
   1401 		tagged = mode_tree_count_tagged(data->data);
   1402 		if (tagged != 0)
   1403 			xasprintf(&prompt, "(%u tagged) ", tagged);
   1404 		else
   1405 			xasprintf(&prompt, "(current) ");
   1406 		data->references++;
   1407 		status_prompt_set(c, NULL, prompt, "",
   1408 		    window_tree_command_callback, window_tree_command_free,
   1409 		    data, PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
   1410 		free(prompt);
   1411 		break;
   1412 	case '\r':
   1413 		name = window_tree_get_target(item, &fs);
   1414 		if (name != NULL)
   1415 			mode_tree_run_command(c, NULL, data->command, name);
   1416 		finished = 1;
   1417 		free(name);
   1418 		break;
   1419 	}
   1420 	if (finished)
   1421 		window_pane_reset_mode(wp);
   1422 	else {
   1423 		mode_tree_draw(data->data);
   1424 		wp->flags |= PANE_REDRAW;
   1425 	}
   1426 }
   1427