xref: /openbsd/usr.bin/tmux/window-customize.c (revision 247fdabf)
1 /* $OpenBSD: window-customize.c,v 1.6 2020/07/27 08:03:10 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "tmux.h"
26 
27 static struct screen	*window_customize_init(struct window_mode_entry *,
28 			     struct cmd_find_state *, struct args *);
29 static void		 window_customize_free(struct window_mode_entry *);
30 static void		 window_customize_resize(struct window_mode_entry *,
31 			      u_int, u_int);
32 static void		 window_customize_key(struct window_mode_entry *,
33 			     struct client *, struct session *,
34 			     struct winlink *, key_code, struct mouse_event *);
35 
36 #define WINDOW_CUSTOMIZE_DEFAULT_FORMAT \
37 	"#{?is_option," \
38 		"#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \
39 		"#[ignore]" \
40 		"#{option_value}#{?option_unit, #{option_unit},}" \
41 	"," \
42 		"#{key}" \
43 	"}"
44 
45 static const struct menu_item window_customize_menu_items[] = {
46 	{ "Select", '\r', NULL },
47 	{ "Expand", KEYC_RIGHT, NULL },
48 	{ "", KEYC_NONE, NULL },
49 	{ "Tag", 't', NULL },
50 	{ "Tag All", '\024', NULL },
51 	{ "Tag None", 'T', NULL },
52 	{ "", KEYC_NONE, NULL },
53 	{ "Cancel", 'q', NULL },
54 
55 	{ NULL, KEYC_NONE, NULL }
56 };
57 
58 const struct window_mode window_customize_mode = {
59 	.name = "options-mode",
60 	.default_format = WINDOW_CUSTOMIZE_DEFAULT_FORMAT,
61 
62 	.init = window_customize_init,
63 	.free = window_customize_free,
64 	.resize = window_customize_resize,
65 	.key = window_customize_key,
66 };
67 
68 enum window_customize_scope {
69 	WINDOW_CUSTOMIZE_NONE,
70 	WINDOW_CUSTOMIZE_KEY,
71 	WINDOW_CUSTOMIZE_SERVER,
72 	WINDOW_CUSTOMIZE_GLOBAL_SESSION,
73 	WINDOW_CUSTOMIZE_SESSION,
74 	WINDOW_CUSTOMIZE_GLOBAL_WINDOW,
75 	WINDOW_CUSTOMIZE_WINDOW,
76 	WINDOW_CUSTOMIZE_PANE
77 };
78 
79 enum window_customize_change {
80 	WINDOW_CUSTOMIZE_UNSET,
81 	WINDOW_CUSTOMIZE_RESET,
82 };
83 
84 struct window_customize_itemdata {
85 	struct window_customize_modedata	*data;
86 	enum window_customize_scope		 scope;
87 
88 	char					*table;
89 	key_code				 key;
90 
91 	struct options				*oo;
92 	char					*name;
93 	int					 idx;
94 };
95 
96 struct window_customize_modedata {
97 	struct window_pane			 *wp;
98 	int					  dead;
99 	int					  references;
100 
101 	struct mode_tree_data			 *data;
102 	char					 *format;
103 	int					  hide_global;
104 
105 	struct window_customize_itemdata	**item_list;
106 	u_int					  item_size;
107 
108 	struct cmd_find_state			  fs;
109 	enum window_customize_change		  change;
110 };
111 
112 static uint64_t
113 window_customize_get_tag(struct options_entry *o, int idx,
114     const struct options_table_entry *oe)
115 {
116 	uint64_t	offset;
117 
118 	if (oe == NULL)
119 		return ((uint64_t)o);
120 	offset = ((char *)oe - (char *)options_table) / sizeof *options_table;
121 	return ((2ULL << 62)|(offset << 32)|((idx + 1) << 1)|1);
122 }
123 
124 static struct options *
125 window_customize_get_tree(enum window_customize_scope scope,
126     struct cmd_find_state *fs)
127 {
128 	switch (scope) {
129 	case WINDOW_CUSTOMIZE_NONE:
130 	case WINDOW_CUSTOMIZE_KEY:
131 		return (NULL);
132 	case WINDOW_CUSTOMIZE_SERVER:
133 		return (global_options);
134 	case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
135 		return (global_s_options);
136 	case WINDOW_CUSTOMIZE_SESSION:
137 		return (fs->s->options);
138 	case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
139 		return (global_w_options);
140 	case WINDOW_CUSTOMIZE_WINDOW:
141 		return (fs->w->options);
142 	case WINDOW_CUSTOMIZE_PANE:
143 		return (fs->wp->options);
144 	}
145 	return (NULL);
146 }
147 
148 static int
149 window_customize_check_item(struct window_customize_modedata *data,
150     struct window_customize_itemdata *item, struct cmd_find_state *fsp)
151 {
152 	struct cmd_find_state	fs;
153 
154 	if (fsp == NULL)
155 		fsp = &fs;
156 
157 	if (cmd_find_valid_state(&data->fs))
158 		cmd_find_copy_state(fsp, &data->fs);
159 	else
160 		cmd_find_from_pane(fsp, data->wp, 0);
161 	return (item->oo == window_customize_get_tree(item->scope, fsp));
162 }
163 
164 static int
165 window_customize_get_key(struct window_customize_itemdata *item,
166     struct key_table **ktp, struct key_binding **bdp)
167 {
168 	struct key_table	*kt;
169 	struct key_binding	*bd;
170 
171 	kt = key_bindings_get_table(item->table, 0);
172 	if (kt == NULL)
173 		return (0);
174 	bd = key_bindings_get(kt, item->key);
175 	if (bd == NULL)
176 		return (0);
177 
178 	if (ktp != NULL)
179 		*ktp = kt;
180 	if (bdp != NULL)
181 		*bdp = bd;
182 	return (1);
183 }
184 
185 static char *
186 window_customize_scope_text(enum window_customize_scope scope,
187     struct cmd_find_state *fs)
188 {
189 	char	*s;
190 	u_int	 idx;
191 
192 	switch (scope) {
193 	case WINDOW_CUSTOMIZE_NONE:
194 	case WINDOW_CUSTOMIZE_KEY:
195 	case WINDOW_CUSTOMIZE_SERVER:
196 	case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
197 	case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
198 		s = xstrdup("");
199 		break;
200 	case WINDOW_CUSTOMIZE_PANE:
201 		window_pane_index(fs->wp, &idx);
202 		xasprintf(&s, "pane %u", idx);
203 		break;
204 	case WINDOW_CUSTOMIZE_SESSION:
205 		xasprintf(&s, "session %s", fs->s->name);
206 		break;
207 	case WINDOW_CUSTOMIZE_WINDOW:
208 		xasprintf(&s, "window %u", fs->wl->idx);
209 		break;
210 	}
211 	return (s);
212 }
213 
214 static struct window_customize_itemdata *
215 window_customize_add_item(struct window_customize_modedata *data)
216 {
217 	struct window_customize_itemdata	*item;
218 
219 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
220 	    sizeof *data->item_list);
221 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
222 	return (item);
223 }
224 
225 static void
226 window_customize_free_item(struct window_customize_itemdata *item)
227 {
228 	free(item->table);
229 	free(item->name);
230 	free(item);
231 }
232 
233 static void
234 window_customize_build_array(struct window_customize_modedata *data,
235     struct mode_tree_item *top, enum window_customize_scope scope,
236     struct options_entry *o, struct format_tree *ft)
237 {
238 	const struct options_table_entry	*oe = options_table_entry(o);
239 	struct options				*oo = options_owner(o);
240 	struct window_customize_itemdata	*item;
241 	struct options_array_item		*ai;
242 	char					*name, *value, *text;
243 	u_int					 idx;
244 	uint64_t				 tag;
245 
246 	ai = options_array_first(o);
247 	while (ai != NULL) {
248 		idx = options_array_item_index(ai);
249 
250 		xasprintf(&name, "%s[%u]", options_name(o), idx);
251 		format_add(ft, "option_name", "%s", name);
252 		value = options_to_string(o, idx, 0);
253 		format_add(ft, "option_value", "%s", value);
254 
255 		item = window_customize_add_item(data);
256 		item->scope = scope;
257 		item->oo = oo;
258 		item->name = xstrdup(options_name(o));
259 		item->idx = idx;
260 
261 		text = format_expand(ft, data->format);
262 		tag = window_customize_get_tag(o, idx, oe);
263 		mode_tree_add(data->data, top, item, tag, name, text, -1);
264 		free(text);
265 
266 		free(name);
267 		free(value);
268 
269 		ai = options_array_next(ai);
270 	}
271 }
272 
273 static void
274 window_customize_build_option(struct window_customize_modedata *data,
275     struct mode_tree_item *top, enum window_customize_scope scope,
276     struct options_entry *o, struct format_tree *ft,
277     const char *filter, struct cmd_find_state *fs)
278 {
279 	const struct options_table_entry	*oe = options_table_entry(o);
280 	struct options				*oo = options_owner(o);
281 	const char				*name = options_name(o);
282 	struct window_customize_itemdata	*item;
283 	char					*text, *expanded, *value;
284 	int					 global = 0, array = 0;
285 	uint64_t				 tag;
286 
287 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
288 		return;
289 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY))
290 		array = 1;
291 
292 	if (scope == WINDOW_CUSTOMIZE_SERVER ||
293 	    scope == WINDOW_CUSTOMIZE_GLOBAL_SESSION ||
294 	    scope == WINDOW_CUSTOMIZE_GLOBAL_WINDOW)
295 		global = 1;
296 	if (data->hide_global && global)
297 		return;
298 
299 	format_add(ft, "option_name", "%s", name);
300 	format_add(ft, "option_is_global", "%d", global);
301 	format_add(ft, "option_is_array", "%d", array);
302 
303 	text = window_customize_scope_text(scope, fs);
304 	format_add(ft, "option_scope", "%s", text);
305 	free(text);
306 
307 	if (oe != NULL && oe->unit != NULL)
308 		format_add(ft, "option_unit", "%s", oe->unit);
309 	else
310 		format_add(ft, "option_unit", "%s", "");
311 
312 	if (!array) {
313 		value = options_to_string(o, -1, 0);
314 		format_add(ft, "option_value", "%s", value);
315 		free(value);
316 	}
317 
318 	if (filter != NULL) {
319 		expanded = format_expand(ft, filter);
320 		if (!format_true(expanded)) {
321 			free(expanded);
322 			return;
323 		}
324 		free(expanded);
325 	}
326 	item = window_customize_add_item(data);
327 	item->oo = oo;
328 	item->scope = scope;
329 	item->name = xstrdup(name);
330 	item->idx = -1;
331 
332 	if (array)
333 		text = NULL;
334 	else
335 		text = format_expand(ft, data->format);
336 	tag = window_customize_get_tag(o, -1, oe);
337 	top = mode_tree_add(data->data, top, item, tag, name, text, 0);
338 	free(text);
339 
340 	if (array)
341 		window_customize_build_array(data, top, scope, o, ft);
342 }
343 
344 static void
345 window_customize_find_user_options(struct options *oo, const char ***list,
346     u_int *size)
347 {
348 	struct options_entry	*o;
349 	const char		*name;
350 	u_int			 i;
351 
352 	o = options_first(oo);
353 	while (o != NULL) {
354 		name = options_name(o);
355 		if (*name != '@') {
356 			o = options_next(o);
357 			continue;
358 		}
359 		for (i = 0; i < *size; i++) {
360 			if (strcmp((*list)[i], name) == 0)
361 				break;
362 		}
363 		if (i != *size) {
364 			o = options_next(o);
365 			continue;
366 		}
367 		*list = xreallocarray(*list, (*size) + 1, sizeof **list);
368 		(*list)[(*size)++] = name;
369 
370 		o = options_next(o);
371 	}
372 }
373 
374 static void
375 window_customize_build_options(struct window_customize_modedata *data,
376     const char *title, uint64_t tag,
377     enum window_customize_scope scope0, struct options *oo0,
378     enum window_customize_scope scope1, struct options *oo1,
379     enum window_customize_scope scope2, struct options *oo2,
380     struct format_tree *ft, const char *filter, struct cmd_find_state *fs)
381 {
382 	struct mode_tree_item		 *top;
383 	struct options_entry		 *o, *loop;
384 	const char			**list = NULL, *name;
385 	u_int				  size = 0, i;
386 	enum window_customize_scope	  scope;
387 
388 	top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
389 	mode_tree_no_tag(top);
390 
391 	/*
392 	 * We get the options from the first tree, but build it using the
393 	 * values from the other two. Any tree can have user options so we need
394 	 * to build a separate list of them.
395 	 */
396 
397 	window_customize_find_user_options(oo0, &list, &size);
398 	if (oo1 != NULL)
399 		window_customize_find_user_options(oo1, &list, &size);
400 	if (oo2 != NULL)
401 		window_customize_find_user_options(oo2, &list, &size);
402 
403 	for (i = 0; i < size; i++) {
404 		if (oo2 != NULL)
405 			o = options_get(oo0, list[i]);
406 		if (o == NULL && oo1 != NULL)
407 			o = options_get(oo1, list[i]);
408 		if (o == NULL)
409 			o = options_get(oo2, list[i]);
410 		if (options_owner(o) == oo2)
411 			scope = scope2;
412 		else if (options_owner(o) == oo1)
413 			scope = scope1;
414 		else
415 			scope = scope0;
416 		window_customize_build_option(data, top, scope, o, ft, filter,
417 		    fs);
418 	}
419 	free(list);
420 
421 	loop = options_first(oo0);
422 	while (loop != NULL) {
423 		name = options_name(loop);
424 		if (*name == '@') {
425 			loop = options_next(loop);
426 			continue;
427 		}
428 		if (oo2 != NULL)
429 			o = options_get(oo2, name);
430 		else if (oo1 != NULL)
431 			o = options_get(oo1, name);
432 		else
433 			o = loop;
434 		if (options_owner(o) == oo2)
435 			scope = scope2;
436 		else if (options_owner(o) == oo1)
437 			scope = scope1;
438 		else
439 			scope = scope0;
440 		window_customize_build_option(data, top, scope, o, ft, filter,
441 		    fs);
442 		loop = options_next(loop);
443 	}
444 }
445 
446 static void
447 window_customize_build_keys(struct window_customize_modedata *data,
448     struct key_table *kt, struct format_tree *ft, const char *filter,
449     struct cmd_find_state *fs, u_int number)
450 {
451 	struct mode_tree_item			*top, *child, *mti;
452 	struct window_customize_itemdata	*item;
453 	struct key_binding			*bd;
454 	char					*title, *text, *tmp, *expanded;
455 	const char				*flag;
456 	uint64_t				 tag;
457 
458 	tag = (1ULL << 62)|((uint64_t)number << 54)|1;
459 
460 	xasprintf(&title, "Key Table - %s", kt->name);
461 	top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
462 	mode_tree_no_tag(top);
463 	free(title);
464 
465 	ft = format_create_from_state(NULL, NULL, fs);
466 	format_add(ft, "is_option", "0");
467 	format_add(ft, "is_key", "1");
468 
469 	bd = key_bindings_first(kt);
470 	while (bd != NULL) {
471 		format_add(ft, "key", "%s", key_string_lookup_key(bd->key, 0));
472 		if (bd->note != NULL)
473 			format_add(ft, "key_note", "%s", bd->note);
474 		if (filter != NULL) {
475 			expanded = format_expand(ft, filter);
476 			if (!format_true(expanded)) {
477 				free(expanded);
478 				continue;
479 			}
480 			free(expanded);
481 		}
482 
483 		item = window_customize_add_item(data);
484 		item->scope = WINDOW_CUSTOMIZE_KEY;
485 		item->table = xstrdup(kt->name);
486 		item->key = bd->key;
487 		item->name = xstrdup(key_string_lookup_key(item->key, 0));
488 		item->idx = -1;
489 
490 		expanded = format_expand(ft, data->format);
491 		child = mode_tree_add(data->data, top, item, (uint64_t)bd,
492 		    expanded, NULL, 0);
493 		free(expanded);
494 
495 		tmp = cmd_list_print(bd->cmdlist, 0);
496 		xasprintf(&text, "#[ignore]%s", tmp);
497 		free(tmp);
498 		mti = mode_tree_add(data->data, child, item,
499 		    tag|(bd->key << 3)|(0 << 1)|1, "Command", text, -1);
500 		mode_tree_draw_as_parent(mti);
501 		mode_tree_no_tag(mti);
502 		free(text);
503 
504 		if (bd->note != NULL)
505 			xasprintf(&text, "#[ignore]%s", bd->note);
506 		else
507 			text = xstrdup("");
508 		mti = mode_tree_add(data->data, child, item,
509 		    tag|(bd->key << 3)|(1 << 1)|1, "Note", text, -1);
510 		mode_tree_draw_as_parent(mti);
511 		mode_tree_no_tag(mti);
512 		free(text);
513 
514 		if (bd->flags & KEY_BINDING_REPEAT)
515 			flag = "on";
516 		else
517 			flag = "off";
518 		mti = mode_tree_add(data->data, child, item,
519 		    tag|(bd->key << 3)|(2 << 1)|1, "Repeat", flag, -1);
520 		mode_tree_draw_as_parent(mti);
521 		mode_tree_no_tag(mti);
522 
523 		bd = key_bindings_next(kt, bd);
524 	}
525 
526 	format_free(ft);
527 }
528 
529 static void
530 window_customize_build(void *modedata,
531     __unused struct mode_tree_sort_criteria *sort_crit, __unused uint64_t *tag,
532     const char *filter)
533 {
534 	struct window_customize_modedata	*data = modedata;
535 	struct cmd_find_state			 fs;
536 	struct format_tree			*ft;
537 	u_int					 i;
538 	struct key_table			*kt;
539 
540 	for (i = 0; i < data->item_size; i++)
541 		window_customize_free_item(data->item_list[i]);
542 	free(data->item_list);
543 	data->item_list = NULL;
544 	data->item_size = 0;
545 
546 	if (cmd_find_valid_state(&data->fs))
547 		cmd_find_copy_state(&fs, &data->fs);
548 	else
549 		cmd_find_from_pane(&fs, data->wp, 0);
550 
551 	ft = format_create_from_state(NULL, NULL, &fs);
552 	format_add(ft, "is_option", "1");
553 	format_add(ft, "is_key", "0");
554 
555 	window_customize_build_options(data, "Server Options",
556 	    (3ULL << 62)|(OPTIONS_TABLE_SERVER << 1)|1,
557 	    WINDOW_CUSTOMIZE_SERVER, global_options,
558 	    WINDOW_CUSTOMIZE_NONE, NULL,
559 	    WINDOW_CUSTOMIZE_NONE, NULL,
560 	    ft, filter, &fs);
561 	window_customize_build_options(data, "Session Options",
562 	    (3ULL << 62)|(OPTIONS_TABLE_SESSION << 1)|1,
563 	    WINDOW_CUSTOMIZE_GLOBAL_SESSION, global_s_options,
564 	    WINDOW_CUSTOMIZE_SESSION, fs.s->options,
565 	    WINDOW_CUSTOMIZE_NONE, NULL,
566 	    ft, filter, &fs);
567 	window_customize_build_options(data, "Window & Pane Options",
568 	    (3ULL << 62)|(OPTIONS_TABLE_WINDOW << 1)|1,
569 	    WINDOW_CUSTOMIZE_GLOBAL_WINDOW, global_w_options,
570 	    WINDOW_CUSTOMIZE_WINDOW, fs.w->options,
571 	    WINDOW_CUSTOMIZE_PANE, fs.wp->options,
572 	    ft, filter, &fs);
573 
574 	format_free(ft);
575 	ft = format_create_from_state(NULL, NULL, &fs);
576 
577 	i = 0;
578 	kt = key_bindings_first_table();
579 	while (kt != NULL) {
580 		if (!RB_EMPTY(&kt->key_bindings)) {
581 			window_customize_build_keys(data, kt, ft, filter, &fs,
582 			    i);
583 			if (++i == 256)
584 				break;
585 		}
586 		kt = key_bindings_next_table(kt);
587 	}
588 
589 	format_free(ft);
590 }
591 
592 static void
593 window_customize_draw_key(__unused struct window_customize_modedata *data,
594     struct window_customize_itemdata *item, struct screen_write_ctx *ctx,
595     u_int sx, u_int sy)
596 {
597 	struct screen		*s = ctx->s;
598 	u_int			 cx = s->cx, cy = s->cy;
599 	struct key_table	*kt;
600 	struct key_binding	*bd, *default_bd;
601 	const char		*note, *period = "";
602 	char			*cmd, *default_cmd;
603 
604 	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
605 		return;
606 
607 	note = bd->note;
608 	if (note == NULL)
609 		note = "There is no note for this key.";
610 	if (*note != '\0' && note[strlen (note) - 1] != '.')
611 		period = ".";
612 	if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s%s",
613 	    note, period))
614 		return;
615 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
616 	if (s->cy >= cy + sy - 1)
617 		return;
618 
619 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
620 	    &grid_default_cell, "This key is in the %s table.", kt->name))
621 		return;
622 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
623 	    &grid_default_cell, "This key %s repeat.",
624 	    (bd->flags & KEY_BINDING_REPEAT) ? "does" : "does not"))
625 		return;
626 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
627 	if (s->cy >= cy + sy - 1)
628 		return;
629 
630 	cmd = cmd_list_print(bd->cmdlist, 0);
631 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
632 	    &grid_default_cell, "Command: %s", cmd)) {
633 		free(cmd);
634 		return;
635 	}
636 	default_bd = key_bindings_get_default(kt, bd->key);
637 	if (default_bd != NULL) {
638 		default_cmd = cmd_list_print(default_bd->cmdlist, 0);
639 		if (strcmp(cmd, default_cmd) != 0 &&
640 		    !screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
641 		    &grid_default_cell, "The default is: %s", default_cmd)) {
642 			free(default_cmd);
643 			free(cmd);
644 			return;
645 		}
646 		free(default_cmd);
647 	}
648 	free(cmd);
649 }
650 
651 static void
652 window_customize_draw_option(struct window_customize_modedata *data,
653     struct window_customize_itemdata *item, struct screen_write_ctx *ctx,
654     u_int sx, u_int sy)
655 {
656 	struct screen				 *s = ctx->s;
657 	u_int					  cx = s->cx, cy = s->cy;
658 	int					  idx;
659 	struct options_entry			 *o, *parent;
660 	struct options				 *go, *wo;
661 	const struct options_table_entry	 *oe;
662 	struct grid_cell			  gc;
663 	const char				**choice, *text, *name;
664 	const char				 *space = "", *unit = "";
665 	char					 *value = NULL, *expanded;
666 	char					 *default_value = NULL;
667 	char					  choices[256] = "";
668 	struct cmd_find_state			  fs;
669 	struct format_tree			 *ft;
670 
671 	if (!window_customize_check_item(data, item, &fs))
672 		return;
673 	name = item->name;
674 	idx = item->idx;
675 
676 	o = options_get(item->oo, name);
677 	if (o == NULL)
678 		return;
679 	oe = options_table_entry(o);
680 
681 	if (oe != NULL && oe->unit != NULL) {
682 		space = " ";
683 		unit = oe->unit;
684 	}
685 	ft = format_create_from_state(NULL, NULL, &fs);
686 
687 	if (oe == NULL)
688 		text = "This is a user option.";
689 	else if (oe->text == NULL)
690 		text = "This option doesn't have a description.";
691 	else
692 		text = oe->text;
693 	if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s",
694 	    text))
695 		goto out;
696 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
697 	if (s->cy >= cy + sy - 1)
698 		goto out;
699 
700 	if (oe == NULL)
701 		text = "user";
702 	else if ((oe->scope & (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) ==
703 	    (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE))
704 		text = "window and pane";
705 	else if (oe->scope & OPTIONS_TABLE_WINDOW)
706 		text = "window";
707 	else if (oe->scope & OPTIONS_TABLE_SESSION)
708 		text = "session";
709 	else
710 		text = "server";
711 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
712 	    &grid_default_cell, "This is a %s option.", text))
713 		goto out;
714 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
715 		if (idx != -1) {
716 			if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
717 			    0, &grid_default_cell,
718 			    "This is an array option, index %u.", idx))
719 				goto out;
720 		} else {
721 			if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
722 			    0, &grid_default_cell, "This is an array option."))
723 				goto out;
724 		}
725 		if (idx == -1)
726 			goto out;
727 	}
728 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
729 	if (s->cy >= cy + sy - 1)
730 		goto out;
731 
732 	value = options_to_string(o, idx, 0);
733 	if (oe != NULL && idx == -1) {
734 		default_value = options_default_to_string(oe);
735 		if (strcmp(default_value, value) == 0) {
736 			free(default_value);
737 			default_value = NULL;
738 		}
739 	}
740 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
741 	    &grid_default_cell, "Option value: %s%s%s", value, space, unit))
742 		goto out;
743 	if (oe == NULL || oe->type == OPTIONS_TABLE_STRING) {
744 		expanded = format_expand(ft, value);
745 		if (strcmp(expanded, value) != 0) {
746 			if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
747 			    0, &grid_default_cell, "This expands to: %s",
748 			    expanded))
749 				goto out;
750 		}
751 		free(expanded);
752 	}
753 	if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) {
754 		for (choice = oe->choices; *choice != NULL; choice++) {
755 			strlcat(choices, *choice, sizeof choices);
756 			strlcat(choices, ", ", sizeof choices);
757 		}
758 		choices[strlen(choices) - 2] = '\0';
759 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
760 		    &grid_default_cell, "Available values are: %s",
761 		    choices))
762 			goto out;
763 	}
764 	if (oe != NULL && oe->type == OPTIONS_TABLE_COLOUR) {
765 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
766 		    &grid_default_cell, "This is a colour option: "))
767 			goto out;
768 		memcpy(&gc, &grid_default_cell, sizeof gc);
769 		gc.fg = options_get_number(item->oo, name);
770 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
771 		    "EXAMPLE"))
772 			goto out;
773 	}
774 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_STYLE)) {
775 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
776 		    &grid_default_cell, "This is a style option: "))
777 			goto out;
778 		style_apply(&gc, item->oo, name, ft);
779 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
780 		    "EXAMPLE"))
781 			goto out;
782 	}
783 	if (default_value != NULL) {
784 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
785 		    &grid_default_cell, "The default is: %s%s%s", default_value,
786 		    space, unit))
787 			goto out;
788 	}
789 
790 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
791 	if (s->cy > cy + sy - 1)
792 		goto out;
793 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
794 		wo = NULL;
795 		go = NULL;
796 	} else {
797 		switch (item->scope) {
798 		case WINDOW_CUSTOMIZE_PANE:
799 			wo = options_get_parent(item->oo);
800 			go = options_get_parent(wo);
801 			break;
802 		case WINDOW_CUSTOMIZE_WINDOW:
803 		case WINDOW_CUSTOMIZE_SESSION:
804 			wo = NULL;
805 			go = options_get_parent(item->oo);
806 			break;
807 		default:
808 			wo = NULL;
809 			go = NULL;
810 			break;
811 		}
812 	}
813 	if (wo != NULL && options_owner(o) != wo) {
814 		parent = options_get_only(wo, name);
815 		if (parent != NULL) {
816 			value = options_to_string(parent, -1 , 0);
817 			if (!screen_write_text(ctx, s->cx, sx,
818 			    sy - (s->cy - cy), 0, &grid_default_cell,
819 			    "Window value (from window %u): %s%s%s", fs.wl->idx,
820 			    value, space, unit))
821 				goto out;
822 		}
823 	}
824 	if (go != NULL && options_owner(o) != go) {
825 		parent = options_get_only(go, name);
826 		if (parent != NULL) {
827 			value = options_to_string(parent, -1 , 0);
828 			if (!screen_write_text(ctx, s->cx, sx,
829 			    sy - (s->cy - cy), 0, &grid_default_cell,
830 			    "Global value: %s%s%s", value, space, unit))
831 				goto out;
832 		}
833 	}
834 
835 out:
836 	free(value);
837 	free(default_value);
838 	format_free(ft);
839 }
840 
841 static void
842 window_customize_draw(void *modedata, void *itemdata,
843     struct screen_write_ctx *ctx, u_int sx, u_int sy)
844 {
845 	struct window_customize_modedata	*data = modedata;
846 	struct window_customize_itemdata	*item = itemdata;
847 
848 	if (item == NULL)
849 		return;
850 
851 	if (item->scope == WINDOW_CUSTOMIZE_KEY)
852 		window_customize_draw_key(data, item, ctx, sx, sy);
853 	else
854 		window_customize_draw_option(data, item, ctx, sx, sy);
855 }
856 
857 static void
858 window_customize_menu(void *modedata, struct client *c, key_code key)
859 {
860 	struct window_customize_modedata	*data = modedata;
861 	struct window_pane			*wp = data->wp;
862 	struct window_mode_entry		*wme;
863 
864 	wme = TAILQ_FIRST(&wp->modes);
865 	if (wme == NULL || wme->data != modedata)
866 		return;
867 	window_customize_key(wme, c, NULL, NULL, key, NULL);
868 }
869 
870 static u_int
871 window_customize_height(__unused void *modedata, __unused u_int height)
872 {
873 	return (12);
874 }
875 
876 static struct screen *
877 window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
878     struct args *args)
879 {
880 	struct window_pane			*wp = wme->wp;
881 	struct window_customize_modedata	*data;
882 	struct screen				*s;
883 
884 	wme->data = data = xcalloc(1, sizeof *data);
885 	data->wp = wp;
886 	data->references = 1;
887 
888 	memcpy(&data->fs, fs, sizeof data->fs);
889 
890 	if (args == NULL || !args_has(args, 'F'))
891 		data->format = xstrdup(WINDOW_CUSTOMIZE_DEFAULT_FORMAT);
892 	else
893 		data->format = xstrdup(args_get(args, 'F'));
894 
895 	data->data = mode_tree_start(wp, args, window_customize_build,
896 	    window_customize_draw, NULL, window_customize_menu,
897 	    window_customize_height, data, window_customize_menu_items, NULL, 0,
898 	    &s);
899 	mode_tree_zoom(data->data, args);
900 
901 	mode_tree_build(data->data);
902 	mode_tree_draw(data->data);
903 
904 	return (s);
905 }
906 
907 static void
908 window_customize_destroy(struct window_customize_modedata *data)
909 {
910 	u_int	i;
911 
912 	if (--data->references != 0)
913 		return;
914 
915 	for (i = 0; i < data->item_size; i++)
916 		window_customize_free_item(data->item_list[i]);
917 	free(data->item_list);
918 
919 	free(data->format);
920 
921 	free(data);
922 }
923 
924 static void
925 window_customize_free(struct window_mode_entry *wme)
926 {
927 	struct window_customize_modedata *data = wme->data;
928 
929 	if (data == NULL)
930 		return;
931 
932 	data->dead = 1;
933 	mode_tree_free(data->data);
934 	window_customize_destroy(data);
935 }
936 
937 static void
938 window_customize_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
939 {
940 	struct window_customize_modedata	*data = wme->data;
941 
942 	mode_tree_resize(data->data, sx, sy);
943 }
944 
945 static void
946 window_customize_free_callback(void *modedata)
947 {
948 	window_customize_destroy(modedata);
949 }
950 
951 static void
952 window_customize_free_item_callback(void *itemdata)
953 {
954 	struct window_customize_itemdata	*item = itemdata;
955 	struct window_customize_modedata	*data = item->data;
956 
957 	window_customize_free_item(item);
958 	window_customize_destroy(data);
959 }
960 
961 static int
962 window_customize_set_option_callback(struct client *c, void *itemdata,
963     const char *s, __unused int done)
964 {
965 	struct window_customize_itemdata	*item = itemdata;
966 	struct window_customize_modedata	*data = item->data;
967 	struct options_entry			*o;
968 	const struct options_table_entry	*oe;
969 	struct options				*oo = item->oo;
970 	const char				*name = item->name;
971 	char					*cause;
972 	int					 idx = item->idx;
973 
974 	if (s == NULL || *s == '\0' || data->dead)
975 		return (0);
976 	if (item == NULL || !window_customize_check_item(data, item, NULL))
977 		return (0);
978 	o = options_get(oo, name);
979 	if (o == NULL)
980 		return (0);
981 	oe = options_table_entry(o);
982 
983 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
984 		if (idx == -1) {
985 			for (idx = 0; idx < INT_MAX; idx++) {
986 				if (options_array_get(o, idx) == NULL)
987 					break;
988 			}
989 		}
990 		if (options_array_set(o, idx, s, 0, &cause) != 0)
991 			goto fail;
992 	} else {
993 		if (options_from_string(oo, oe, name, s, 0, &cause) != 0)
994 			goto fail;
995 	}
996 
997 	options_push_changes(item->name);
998 	mode_tree_build(data->data);
999 	mode_tree_draw(data->data);
1000 	data->wp->flags |= PANE_REDRAW;
1001 
1002 	return (0);
1003 
1004 fail:
1005 	*cause = toupper((u_char)*cause);
1006 	status_message_set(c, -1, 1, "%s", cause);
1007 	free(cause);
1008 	return (0);
1009 }
1010 
1011 static void
1012 window_customize_set_option(struct client *c,
1013     struct window_customize_modedata *data,
1014     struct window_customize_itemdata *item, int global, int pane)
1015 {
1016 	struct options_entry			*o;
1017 	const struct options_table_entry	*oe;
1018 	struct options				*oo;
1019 	struct window_customize_itemdata	*new_item;
1020 	int					 flag, idx = item->idx;
1021 	enum window_customize_scope		 scope;
1022 	u_int					 choice;
1023 	const char				*name = item->name, *space = "";
1024 	char					*prompt, *value, *text;
1025 	struct cmd_find_state			 fs;
1026 
1027 	if (item == NULL || !window_customize_check_item(data, item, &fs))
1028 		return;
1029 	o = options_get(item->oo, name);
1030 	if (o == NULL)
1031 		return;
1032 
1033 	oe = options_table_entry(o);
1034 	if (~oe->scope & OPTIONS_TABLE_PANE)
1035 		pane = 0;
1036 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
1037 		scope = item->scope;
1038 		oo = item->oo;
1039 	} else {
1040 		if (global) {
1041 			switch (item->scope) {
1042 			case WINDOW_CUSTOMIZE_NONE:
1043 			case WINDOW_CUSTOMIZE_KEY:
1044 			case WINDOW_CUSTOMIZE_SERVER:
1045 			case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
1046 			case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
1047 				scope = item->scope;
1048 				break;
1049 			case WINDOW_CUSTOMIZE_SESSION:
1050 				scope = WINDOW_CUSTOMIZE_GLOBAL_SESSION;
1051 				break;
1052 			case WINDOW_CUSTOMIZE_WINDOW:
1053 			case WINDOW_CUSTOMIZE_PANE:
1054 				scope = WINDOW_CUSTOMIZE_GLOBAL_WINDOW;
1055 				break;
1056 			}
1057 		} else {
1058 			switch (item->scope) {
1059 			case WINDOW_CUSTOMIZE_NONE:
1060 			case WINDOW_CUSTOMIZE_KEY:
1061 			case WINDOW_CUSTOMIZE_SERVER:
1062 			case WINDOW_CUSTOMIZE_SESSION:
1063 				scope = item->scope;
1064 				break;
1065 			case WINDOW_CUSTOMIZE_WINDOW:
1066 			case WINDOW_CUSTOMIZE_PANE:
1067 				if (pane)
1068 					scope = WINDOW_CUSTOMIZE_PANE;
1069 				else
1070 					scope = WINDOW_CUSTOMIZE_WINDOW;
1071 				break;
1072 			case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
1073 				scope = WINDOW_CUSTOMIZE_SESSION;
1074 				break;
1075 			case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
1076 				if (pane)
1077 					scope = WINDOW_CUSTOMIZE_PANE;
1078 				else
1079 					scope = WINDOW_CUSTOMIZE_WINDOW;
1080 				break;
1081 			}
1082 		}
1083 		if (scope == item->scope)
1084 			oo = item->oo;
1085 		else
1086 			oo = window_customize_get_tree(scope, &fs);
1087 	}
1088 
1089 	if (oe != NULL && oe->type == OPTIONS_TABLE_FLAG) {
1090 		flag = options_get_number(oo, name);
1091 		options_set_number(oo, name, !flag);
1092 	} else if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) {
1093 		choice = options_get_number(oo, name);
1094 		if (oe->choices[choice + 1] == NULL)
1095 			choice = 0;
1096 		else
1097 			choice++;
1098 		options_set_number(oo, name, choice);
1099 	} else {
1100 		text = window_customize_scope_text(scope, &fs);
1101 		if (*text != '\0')
1102 			space = ", for ";
1103 		else if (scope != WINDOW_CUSTOMIZE_SERVER)
1104 			space = ", global";
1105 		if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
1106 			if (idx == -1) {
1107 				xasprintf(&prompt, "(%s[+]%s%s) ", name, space,
1108 				    text);
1109 			} else {
1110 				xasprintf(&prompt, "(%s[%d]%s%s) ", name, idx,
1111 				    space, text);
1112 			}
1113 		} else
1114 			xasprintf(&prompt, "(%s%s%s) ", name, space, text);
1115 		free(text);
1116 
1117 		value = options_to_string(o, idx, 0);
1118 
1119 		new_item = xcalloc(1, sizeof *new_item);
1120 		new_item->data = data;
1121 		new_item->scope = scope;
1122 		new_item->oo = oo;
1123 		new_item->name = xstrdup(name);
1124 		new_item->idx = idx;
1125 
1126 		data->references++;
1127 		status_prompt_set(c, NULL, prompt, value,
1128 		    window_customize_set_option_callback,
1129 		    window_customize_free_item_callback, new_item,
1130 		    PROMPT_NOFORMAT);
1131 
1132 		free(prompt);
1133 		free(value);
1134 	}
1135 }
1136 
1137 static void
1138 window_customize_unset_option(struct window_customize_modedata *data,
1139     struct window_customize_itemdata *item)
1140 {
1141 	struct options_entry	*o;
1142 
1143 	if (item == NULL || !window_customize_check_item(data, item, NULL))
1144 		return;
1145 
1146 	o = options_get(item->oo, item->name);
1147 	if (o == NULL)
1148 		return;
1149 	if (item->idx != -1 && item == mode_tree_get_current(data->data))
1150 		mode_tree_up(data->data, 0);
1151 	options_remove_or_default(o, item->idx, NULL);
1152 }
1153 
1154 static void
1155 window_customize_reset_option(struct window_customize_modedata *data,
1156     struct window_customize_itemdata *item)
1157 {
1158 	struct options		*oo;
1159 	struct options_entry	*o;
1160 
1161 	if (item == NULL || !window_customize_check_item(data, item, NULL))
1162 		return;
1163 	if (item->idx != -1)
1164 		return;
1165 
1166 	oo = item->oo;
1167 	while (oo != NULL) {
1168 		o = options_get_only(item->oo, item->name);
1169 		if (o != NULL)
1170 			options_remove_or_default(o, -1, NULL);
1171 		oo = options_get_parent(oo);
1172 	}
1173 }
1174 
1175 static int
1176 window_customize_set_command_callback(struct client *c, void *itemdata,
1177     const char *s, __unused int done)
1178 {
1179 	struct window_customize_itemdata	*item = itemdata;
1180 	struct window_customize_modedata	*data = item->data;
1181 	struct key_binding			*bd;
1182 	struct cmd_parse_result			*pr;
1183 	char					*error;
1184 
1185 	if (s == NULL || *s == '\0' || data->dead)
1186 		return (0);
1187 	if (item == NULL || !window_customize_get_key(item, NULL, &bd))
1188 		return (0);
1189 
1190 	pr = cmd_parse_from_string(s, NULL);
1191 	switch (pr->status) {
1192 	case CMD_PARSE_EMPTY:
1193 		error = xstrdup("empty command");
1194 		goto fail;
1195 	case CMD_PARSE_ERROR:
1196 		error = pr->error;
1197 		goto fail;
1198 	case CMD_PARSE_SUCCESS:
1199 		break;
1200 	}
1201 	cmd_list_free(bd->cmdlist);
1202 	bd->cmdlist = pr->cmdlist;
1203 
1204 	mode_tree_build(data->data);
1205 	mode_tree_draw(data->data);
1206 	data->wp->flags |= PANE_REDRAW;
1207 
1208 	return (0);
1209 
1210 fail:
1211 	*error = toupper((u_char)*error);
1212 	status_message_set(c, -1, 1, "%s", error);
1213 	free(error);
1214 	return (0);
1215 }
1216 
1217 static int
1218 window_customize_set_note_callback(__unused struct client *c, void *itemdata,
1219     const char *s, __unused int done)
1220 {
1221 	struct window_customize_itemdata	*item = itemdata;
1222 	struct window_customize_modedata	*data = item->data;
1223 	struct key_binding			*bd;
1224 
1225 	if (s == NULL || *s == '\0' || data->dead)
1226 		return (0);
1227 	if (item == NULL || !window_customize_get_key(item, NULL, &bd))
1228 		return (0);
1229 
1230 	free((void *)bd->note);
1231 	bd->note = xstrdup(s);
1232 
1233 	mode_tree_build(data->data);
1234 	mode_tree_draw(data->data);
1235 	data->wp->flags |= PANE_REDRAW;
1236 
1237 	return (0);
1238 }
1239 
1240 static void
1241 window_customize_set_key(struct client *c,
1242     struct window_customize_modedata *data,
1243     struct window_customize_itemdata *item)
1244 {
1245 	key_code				 key = item->key;
1246 	struct key_binding			*bd;
1247 	const char				*s;
1248 	char					*prompt, *value;
1249 	struct window_customize_itemdata	*new_item;
1250 
1251 	if (item == NULL || !window_customize_get_key(item, NULL, &bd))
1252 		return;
1253 
1254 	s = mode_tree_get_current_name(data->data);
1255 	if (strcmp(s, "Repeat") == 0)
1256 		bd->flags ^= KEY_BINDING_REPEAT;
1257 	else if (strcmp(s, "Command") == 0) {
1258 		xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0));
1259 		value = cmd_list_print(bd->cmdlist, 0);
1260 
1261 		new_item = xcalloc(1, sizeof *new_item);
1262 		new_item->data = data;
1263 		new_item->scope = item->scope;
1264 		new_item->table = xstrdup(item->table);
1265 		new_item->key = key;
1266 
1267 		data->references++;
1268 		status_prompt_set(c, NULL, prompt, value,
1269 		    window_customize_set_command_callback,
1270 		    window_customize_free_item_callback, new_item,
1271 		    PROMPT_NOFORMAT);
1272 		free(prompt);
1273 		free(value);
1274 	} else if (strcmp(s, "Note") == 0) {
1275 		xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0));
1276 
1277 		new_item = xcalloc(1, sizeof *new_item);
1278 		new_item->data = data;
1279 		new_item->scope = item->scope;
1280 		new_item->table = xstrdup(item->table);
1281 		new_item->key = key;
1282 
1283 		data->references++;
1284 		status_prompt_set(c, NULL, prompt,
1285 		    (bd->note == NULL ? "" : bd->note),
1286 		    window_customize_set_note_callback,
1287 		    window_customize_free_item_callback, new_item,
1288 		    PROMPT_NOFORMAT);
1289 		free(prompt);
1290 	}
1291 }
1292 
1293 static void
1294 window_customize_unset_key(struct window_customize_modedata *data,
1295     struct window_customize_itemdata *item)
1296 {
1297 	struct key_table	*kt;
1298 	struct key_binding	*bd;
1299 
1300 	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
1301 		return;
1302 
1303 	if (item == mode_tree_get_current(data->data)) {
1304 		mode_tree_collapse_current(data->data);
1305 		mode_tree_up(data->data, 0);
1306 	}
1307 	key_bindings_remove(kt->name, bd->key);
1308 }
1309 
1310 static void
1311 window_customize_reset_key(struct window_customize_modedata *data,
1312     struct window_customize_itemdata *item)
1313 {
1314 	struct key_table	*kt;
1315 	struct key_binding	*dd, *bd;
1316 
1317 	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
1318 		return;
1319 
1320 	dd = key_bindings_get_default(kt, bd->key);
1321 	if (dd != NULL && bd->cmdlist == dd->cmdlist)
1322 		return;
1323 	if (dd == NULL && item == mode_tree_get_current(data->data)) {
1324 		mode_tree_collapse_current(data->data);
1325 		mode_tree_up(data->data, 0);
1326 	}
1327 	key_bindings_reset(kt->name, bd->key);
1328 }
1329 
1330 static void
1331 window_customize_change_each(void *modedata, void *itemdata,
1332     __unused struct client *c, __unused key_code key)
1333 {
1334 	struct window_customize_modedata	*data = modedata;
1335 	struct window_customize_itemdata	*item = itemdata;
1336 
1337 	switch (data->change) {
1338 	case WINDOW_CUSTOMIZE_UNSET:
1339 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
1340 			window_customize_unset_key(data, item);
1341 		else
1342 			window_customize_unset_option(data, item);
1343 		break;
1344 	case WINDOW_CUSTOMIZE_RESET:
1345 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
1346 			window_customize_reset_key(data, item);
1347 		else
1348 			window_customize_reset_option(data, item);
1349 		break;
1350 	}
1351 	if (item->scope != WINDOW_CUSTOMIZE_KEY)
1352 		options_push_changes(item->name);
1353 }
1354 
1355 static int
1356 window_customize_change_current_callback(__unused struct client *c,
1357     void *modedata, const char *s, __unused int done)
1358 {
1359 	struct window_customize_modedata	*data = modedata;
1360 	struct window_customize_itemdata	*item;
1361 
1362 	if (s == NULL || *s == '\0' || data->dead)
1363 		return (0);
1364 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
1365 		return (0);
1366 
1367 	item = mode_tree_get_current(data->data);
1368 	switch (data->change) {
1369 	case WINDOW_CUSTOMIZE_UNSET:
1370 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
1371 			window_customize_unset_key(data, item);
1372 		else
1373 			window_customize_unset_option(data, item);
1374 		break;
1375 	case WINDOW_CUSTOMIZE_RESET:
1376 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
1377 			window_customize_reset_key(data, item);
1378 		else
1379 			window_customize_reset_option(data, item);
1380 		break;
1381 	}
1382 	if (item->scope != WINDOW_CUSTOMIZE_KEY)
1383 		options_push_changes(item->name);
1384 	mode_tree_build(data->data);
1385 	mode_tree_draw(data->data);
1386 	data->wp->flags |= PANE_REDRAW;
1387 
1388 	return (0);
1389 }
1390 
1391 static int
1392 window_customize_change_tagged_callback(struct client *c, void *modedata,
1393     const char *s, __unused int done)
1394 {
1395 	struct window_customize_modedata	*data = modedata;
1396 
1397 	if (s == NULL || *s == '\0' || data->dead)
1398 		return (0);
1399 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
1400 		return (0);
1401 
1402 	mode_tree_each_tagged(data->data, window_customize_change_each, c,
1403 	    KEYC_NONE, 0);
1404 	mode_tree_build(data->data);
1405 	mode_tree_draw(data->data);
1406 	data->wp->flags |= PANE_REDRAW;
1407 
1408 	return (0);
1409 }
1410 
1411 static void
1412 window_customize_key(struct window_mode_entry *wme, struct client *c,
1413     __unused struct session *s, __unused struct winlink *wl, key_code key,
1414     struct mouse_event *m)
1415 {
1416 	struct window_pane			*wp = wme->wp;
1417 	struct window_customize_modedata	*data = wme->data;
1418 	struct window_customize_itemdata	*item, *new_item;
1419 	int					 finished, idx;
1420 	char					*prompt;
1421 	u_int					 tagged;
1422 
1423 	item = mode_tree_get_current(data->data);
1424 	finished = mode_tree_key(data->data, c, &key, m, NULL, NULL);
1425 	if (item != (new_item = mode_tree_get_current(data->data)))
1426 		item = new_item;
1427 
1428 	switch (key) {
1429 	case '\r':
1430 	case 's':
1431 		if (item == NULL)
1432 			break;
1433 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
1434 			window_customize_set_key(c, data, item);
1435 		else {
1436 			window_customize_set_option(c, data, item, 0, 1);
1437 			options_push_changes(item->name);
1438 		}
1439 		mode_tree_build(data->data);
1440 		break;
1441 	case 'w':
1442 		if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY)
1443 			break;
1444 		window_customize_set_option(c, data, item, 0, 0);
1445 		options_push_changes(item->name);
1446 		mode_tree_build(data->data);
1447 		break;
1448 	case 'S':
1449 	case 'W':
1450 		if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY)
1451 			break;
1452 		window_customize_set_option(c, data, item, 1, 0);
1453 		options_push_changes(item->name);
1454 		mode_tree_build(data->data);
1455 		break;
1456 	case 'd':
1457 		if (item == NULL || item->idx != -1)
1458 			break;
1459 		xasprintf(&prompt, "Reset %s to default? ", item->name);
1460 		data->references++;
1461 		data->change = WINDOW_CUSTOMIZE_RESET;
1462 		status_prompt_set(c, NULL, prompt, "",
1463 		    window_customize_change_current_callback,
1464 		    window_customize_free_callback, data,
1465 		    PROMPT_SINGLE|PROMPT_NOFORMAT);
1466 		free(prompt);
1467 		break;
1468 	case 'D':
1469 		tagged = mode_tree_count_tagged(data->data);
1470 		if (tagged == 0)
1471 			break;
1472 		xasprintf(&prompt, "Reset %u tagged to default? ", tagged);
1473 		data->references++;
1474 		data->change = WINDOW_CUSTOMIZE_RESET;
1475 		status_prompt_set(c, NULL, prompt, "",
1476 		    window_customize_change_tagged_callback,
1477 		    window_customize_free_callback, data,
1478 		    PROMPT_SINGLE|PROMPT_NOFORMAT);
1479 		free(prompt);
1480 		break;
1481 	case 'u':
1482 		if (item == NULL)
1483 			break;
1484 		idx = item->idx;
1485 		if (idx != -1)
1486 			xasprintf(&prompt, "Unset %s[%d]? ", item->name, idx);
1487 		else
1488 			xasprintf(&prompt, "Unset %s? ", item->name);
1489 		data->references++;
1490 		data->change = WINDOW_CUSTOMIZE_UNSET;
1491 		status_prompt_set(c, NULL, prompt, "",
1492 		    window_customize_change_current_callback,
1493 		    window_customize_free_callback, data,
1494 		    PROMPT_SINGLE|PROMPT_NOFORMAT);
1495 		free(prompt);
1496 		break;
1497 	case 'U':
1498 		tagged = mode_tree_count_tagged(data->data);
1499 		if (tagged == 0)
1500 			break;
1501 		xasprintf(&prompt, "Unset %u tagged? ", tagged);
1502 		data->references++;
1503 		data->change = WINDOW_CUSTOMIZE_UNSET;
1504 		status_prompt_set(c, NULL, prompt, "",
1505 		    window_customize_change_tagged_callback,
1506 		    window_customize_free_callback, data,
1507 		    PROMPT_SINGLE|PROMPT_NOFORMAT);
1508 		free(prompt);
1509 		break;
1510 	case 'H':
1511 		data->hide_global = !data->hide_global;
1512 		mode_tree_build(data->data);
1513 		break;
1514 	}
1515 	if (finished)
1516 		window_pane_reset_mode(wp);
1517 	else {
1518 		mode_tree_draw(data->data);
1519 		wp->flags |= PANE_REDRAW;
1520 	}
1521 }
1522