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