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