xref: /openbsd-src/usr.bin/tmux/window-customize.c (revision af11b61d5d7d6672d789056da9e8e9b2862a20b6)
1*af11b61dSnicm /* $OpenBSD: window-customize.c,v 1.15 2024/10/04 19:16:13 nicm Exp $ */
267c16a7cSnicm 
367c16a7cSnicm /*
467c16a7cSnicm  * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
567c16a7cSnicm  *
667c16a7cSnicm  * Permission to use, copy, modify, and distribute this software for any
767c16a7cSnicm  * purpose with or without fee is hereby granted, provided that the above
867c16a7cSnicm  * copyright notice and this permission notice appear in all copies.
967c16a7cSnicm  *
1067c16a7cSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1167c16a7cSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1267c16a7cSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1367c16a7cSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1467c16a7cSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
1567c16a7cSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
1667c16a7cSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1767c16a7cSnicm  */
1867c16a7cSnicm 
1967c16a7cSnicm #include <sys/types.h>
2067c16a7cSnicm 
2167c16a7cSnicm #include <ctype.h>
2267c16a7cSnicm #include <stdlib.h>
2367c16a7cSnicm #include <string.h>
2467c16a7cSnicm 
2567c16a7cSnicm #include "tmux.h"
2667c16a7cSnicm 
2767c16a7cSnicm static struct screen	*window_customize_init(struct window_mode_entry *,
2867c16a7cSnicm 			     struct cmd_find_state *, struct args *);
2967c16a7cSnicm static void		 window_customize_free(struct window_mode_entry *);
3067c16a7cSnicm static void		 window_customize_resize(struct window_mode_entry *,
3167c16a7cSnicm 			      u_int, u_int);
3267c16a7cSnicm static void		 window_customize_key(struct window_mode_entry *,
3367c16a7cSnicm 			     struct client *, struct session *,
3467c16a7cSnicm 			     struct winlink *, key_code, struct mouse_event *);
3567c16a7cSnicm 
3667c16a7cSnicm #define WINDOW_CUSTOMIZE_DEFAULT_FORMAT \
3767c16a7cSnicm 	"#{?is_option," \
3867c16a7cSnicm 		"#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \
3967c16a7cSnicm 		"#[ignore]" \
4067c16a7cSnicm 		"#{option_value}#{?option_unit, #{option_unit},}" \
4167c16a7cSnicm 	"," \
4267c16a7cSnicm 		"#{key}" \
4367c16a7cSnicm 	"}"
4467c16a7cSnicm 
4567c16a7cSnicm static const struct menu_item window_customize_menu_items[] = {
4667c16a7cSnicm 	{ "Select", '\r', NULL },
4767c16a7cSnicm 	{ "Expand", KEYC_RIGHT, NULL },
4867c16a7cSnicm 	{ "", KEYC_NONE, NULL },
4967c16a7cSnicm 	{ "Tag", 't', NULL },
5067c16a7cSnicm 	{ "Tag All", '\024', NULL },
5167c16a7cSnicm 	{ "Tag None", 'T', NULL },
5267c16a7cSnicm 	{ "", KEYC_NONE, NULL },
5367c16a7cSnicm 	{ "Cancel", 'q', NULL },
5467c16a7cSnicm 
5567c16a7cSnicm 	{ NULL, KEYC_NONE, NULL }
5667c16a7cSnicm };
5767c16a7cSnicm 
5867c16a7cSnicm const struct window_mode window_customize_mode = {
5967c16a7cSnicm 	.name = "options-mode",
6067c16a7cSnicm 	.default_format = WINDOW_CUSTOMIZE_DEFAULT_FORMAT,
6167c16a7cSnicm 
6267c16a7cSnicm 	.init = window_customize_init,
6367c16a7cSnicm 	.free = window_customize_free,
6467c16a7cSnicm 	.resize = window_customize_resize,
6567c16a7cSnicm 	.key = window_customize_key,
6667c16a7cSnicm };
6767c16a7cSnicm 
6867c16a7cSnicm enum window_customize_scope {
6967c16a7cSnicm 	WINDOW_CUSTOMIZE_NONE,
7067c16a7cSnicm 	WINDOW_CUSTOMIZE_KEY,
7167c16a7cSnicm 	WINDOW_CUSTOMIZE_SERVER,
7267c16a7cSnicm 	WINDOW_CUSTOMIZE_GLOBAL_SESSION,
7367c16a7cSnicm 	WINDOW_CUSTOMIZE_SESSION,
7467c16a7cSnicm 	WINDOW_CUSTOMIZE_GLOBAL_WINDOW,
7567c16a7cSnicm 	WINDOW_CUSTOMIZE_WINDOW,
7667c16a7cSnicm 	WINDOW_CUSTOMIZE_PANE
7767c16a7cSnicm };
7867c16a7cSnicm 
7994c0d63cSnicm enum window_customize_change {
8094c0d63cSnicm 	WINDOW_CUSTOMIZE_UNSET,
8194c0d63cSnicm 	WINDOW_CUSTOMIZE_RESET,
8294c0d63cSnicm };
8394c0d63cSnicm 
8467c16a7cSnicm struct window_customize_itemdata {
8567c16a7cSnicm 	struct window_customize_modedata	*data;
8667c16a7cSnicm 	enum window_customize_scope		 scope;
8767c16a7cSnicm 
8867c16a7cSnicm 	char					*table;
8967c16a7cSnicm 	key_code				 key;
9067c16a7cSnicm 
9167c16a7cSnicm 	struct options				*oo;
9267c16a7cSnicm 	char					*name;
9367c16a7cSnicm 	int					 idx;
9467c16a7cSnicm };
9567c16a7cSnicm 
9667c16a7cSnicm struct window_customize_modedata {
9767c16a7cSnicm 	struct window_pane			 *wp;
9867c16a7cSnicm 	int					  dead;
9967c16a7cSnicm 	int					  references;
10067c16a7cSnicm 
10167c16a7cSnicm 	struct mode_tree_data			 *data;
10267c16a7cSnicm 	char					 *format;
10367c16a7cSnicm 	int					  hide_global;
104*af11b61dSnicm 	int					  prompt_flags;
10567c16a7cSnicm 
10667c16a7cSnicm 	struct window_customize_itemdata	**item_list;
10767c16a7cSnicm 	u_int					  item_size;
10867c16a7cSnicm 
10967c16a7cSnicm 	struct cmd_find_state			  fs;
11094c0d63cSnicm 	enum window_customize_change		  change;
11167c16a7cSnicm };
11267c16a7cSnicm 
11367c16a7cSnicm static uint64_t
11467c16a7cSnicm window_customize_get_tag(struct options_entry *o, int idx,
11567c16a7cSnicm     const struct options_table_entry *oe)
11667c16a7cSnicm {
11767c16a7cSnicm 	uint64_t	offset;
11867c16a7cSnicm 
11967c16a7cSnicm 	if (oe == NULL)
12067c16a7cSnicm 		return ((uint64_t)o);
12167c16a7cSnicm 	offset = ((char *)oe - (char *)options_table) / sizeof *options_table;
12267c16a7cSnicm 	return ((2ULL << 62)|(offset << 32)|((idx + 1) << 1)|1);
12367c16a7cSnicm }
12467c16a7cSnicm 
12567c16a7cSnicm static struct options *
12667c16a7cSnicm window_customize_get_tree(enum window_customize_scope scope,
12767c16a7cSnicm     struct cmd_find_state *fs)
12867c16a7cSnicm {
12967c16a7cSnicm 	switch (scope) {
13067c16a7cSnicm 	case WINDOW_CUSTOMIZE_NONE:
13167c16a7cSnicm 	case WINDOW_CUSTOMIZE_KEY:
13267c16a7cSnicm 		return (NULL);
13367c16a7cSnicm 	case WINDOW_CUSTOMIZE_SERVER:
13467c16a7cSnicm 		return (global_options);
13567c16a7cSnicm 	case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
13667c16a7cSnicm 		return (global_s_options);
13767c16a7cSnicm 	case WINDOW_CUSTOMIZE_SESSION:
13867c16a7cSnicm 		return (fs->s->options);
13967c16a7cSnicm 	case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
14067c16a7cSnicm 		return (global_w_options);
14167c16a7cSnicm 	case WINDOW_CUSTOMIZE_WINDOW:
14267c16a7cSnicm 		return (fs->w->options);
14367c16a7cSnicm 	case WINDOW_CUSTOMIZE_PANE:
14467c16a7cSnicm 		return (fs->wp->options);
14567c16a7cSnicm 	}
14667c16a7cSnicm 	return (NULL);
14767c16a7cSnicm }
14867c16a7cSnicm 
14967c16a7cSnicm static int
15067c16a7cSnicm window_customize_check_item(struct window_customize_modedata *data,
15167c16a7cSnicm     struct window_customize_itemdata *item, struct cmd_find_state *fsp)
15267c16a7cSnicm {
15367c16a7cSnicm 	struct cmd_find_state	fs;
15467c16a7cSnicm 
15567c16a7cSnicm 	if (fsp == NULL)
15667c16a7cSnicm 		fsp = &fs;
15767c16a7cSnicm 
15867c16a7cSnicm 	if (cmd_find_valid_state(&data->fs))
15967c16a7cSnicm 		cmd_find_copy_state(fsp, &data->fs);
16067c16a7cSnicm 	else
16167c16a7cSnicm 		cmd_find_from_pane(fsp, data->wp, 0);
16267c16a7cSnicm 	return (item->oo == window_customize_get_tree(item->scope, fsp));
16367c16a7cSnicm }
16467c16a7cSnicm 
16567c16a7cSnicm static int
16667c16a7cSnicm window_customize_get_key(struct window_customize_itemdata *item,
16767c16a7cSnicm     struct key_table **ktp, struct key_binding **bdp)
16867c16a7cSnicm {
16967c16a7cSnicm 	struct key_table	*kt;
17067c16a7cSnicm 	struct key_binding	*bd;
17167c16a7cSnicm 
17267c16a7cSnicm 	kt = key_bindings_get_table(item->table, 0);
17367c16a7cSnicm 	if (kt == NULL)
17467c16a7cSnicm 		return (0);
17567c16a7cSnicm 	bd = key_bindings_get(kt, item->key);
17667c16a7cSnicm 	if (bd == NULL)
17767c16a7cSnicm 		return (0);
17867c16a7cSnicm 
17967c16a7cSnicm 	if (ktp != NULL)
18067c16a7cSnicm 		*ktp = kt;
18167c16a7cSnicm 	if (bdp != NULL)
18267c16a7cSnicm 		*bdp = bd;
18367c16a7cSnicm 	return (1);
18467c16a7cSnicm }
18567c16a7cSnicm 
18667c16a7cSnicm static char *
18767c16a7cSnicm window_customize_scope_text(enum window_customize_scope scope,
18867c16a7cSnicm     struct cmd_find_state *fs)
18967c16a7cSnicm {
19067c16a7cSnicm 	char	*s;
19167c16a7cSnicm 	u_int	 idx;
19267c16a7cSnicm 
19367c16a7cSnicm 	switch (scope) {
19467c16a7cSnicm 	case WINDOW_CUSTOMIZE_PANE:
19567c16a7cSnicm 		window_pane_index(fs->wp, &idx);
19667c16a7cSnicm 		xasprintf(&s, "pane %u", idx);
19767c16a7cSnicm 		break;
19867c16a7cSnicm 	case WINDOW_CUSTOMIZE_SESSION:
19967c16a7cSnicm 		xasprintf(&s, "session %s", fs->s->name);
20067c16a7cSnicm 		break;
20167c16a7cSnicm 	case WINDOW_CUSTOMIZE_WINDOW:
20267c16a7cSnicm 		xasprintf(&s, "window %u", fs->wl->idx);
20367c16a7cSnicm 		break;
2048f1cd816Snicm 	default:
2058f1cd816Snicm 		s = xstrdup("");
2068f1cd816Snicm 		break;
20767c16a7cSnicm 	}
20867c16a7cSnicm 	return (s);
20967c16a7cSnicm }
21067c16a7cSnicm 
21167c16a7cSnicm static struct window_customize_itemdata *
21267c16a7cSnicm window_customize_add_item(struct window_customize_modedata *data)
21367c16a7cSnicm {
21467c16a7cSnicm 	struct window_customize_itemdata	*item;
21567c16a7cSnicm 
21667c16a7cSnicm 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
21767c16a7cSnicm 	    sizeof *data->item_list);
21867c16a7cSnicm 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
21967c16a7cSnicm 	return (item);
22067c16a7cSnicm }
22167c16a7cSnicm 
22267c16a7cSnicm static void
22367c16a7cSnicm window_customize_free_item(struct window_customize_itemdata *item)
22467c16a7cSnicm {
22567c16a7cSnicm 	free(item->table);
22667c16a7cSnicm 	free(item->name);
22767c16a7cSnicm 	free(item);
22867c16a7cSnicm }
22967c16a7cSnicm 
23067c16a7cSnicm static void
23167c16a7cSnicm window_customize_build_array(struct window_customize_modedata *data,
23267c16a7cSnicm     struct mode_tree_item *top, enum window_customize_scope scope,
23367c16a7cSnicm     struct options_entry *o, struct format_tree *ft)
23467c16a7cSnicm {
23567c16a7cSnicm 	const struct options_table_entry	*oe = options_table_entry(o);
23667c16a7cSnicm 	struct options				*oo = options_owner(o);
23767c16a7cSnicm 	struct window_customize_itemdata	*item;
23867c16a7cSnicm 	struct options_array_item		*ai;
23967c16a7cSnicm 	char					*name, *value, *text;
24067c16a7cSnicm 	u_int					 idx;
24167c16a7cSnicm 	uint64_t				 tag;
24267c16a7cSnicm 
24367c16a7cSnicm 	ai = options_array_first(o);
24467c16a7cSnicm 	while (ai != NULL) {
24567c16a7cSnicm 		idx = options_array_item_index(ai);
24667c16a7cSnicm 
24767c16a7cSnicm 		xasprintf(&name, "%s[%u]", options_name(o), idx);
24867c16a7cSnicm 		format_add(ft, "option_name", "%s", name);
24967c16a7cSnicm 		value = options_to_string(o, idx, 0);
25067c16a7cSnicm 		format_add(ft, "option_value", "%s", value);
25167c16a7cSnicm 
25267c16a7cSnicm 		item = window_customize_add_item(data);
25367c16a7cSnicm 		item->scope = scope;
25467c16a7cSnicm 		item->oo = oo;
25567c16a7cSnicm 		item->name = xstrdup(options_name(o));
25667c16a7cSnicm 		item->idx = idx;
25767c16a7cSnicm 
25867c16a7cSnicm 		text = format_expand(ft, data->format);
25967c16a7cSnicm 		tag = window_customize_get_tag(o, idx, oe);
26067c16a7cSnicm 		mode_tree_add(data->data, top, item, tag, name, text, -1);
26167c16a7cSnicm 		free(text);
26267c16a7cSnicm 
26367c16a7cSnicm 		free(name);
26467c16a7cSnicm 		free(value);
26567c16a7cSnicm 
26667c16a7cSnicm 		ai = options_array_next(ai);
26767c16a7cSnicm 	}
26867c16a7cSnicm }
26967c16a7cSnicm 
27067c16a7cSnicm static void
27167c16a7cSnicm window_customize_build_option(struct window_customize_modedata *data,
27267c16a7cSnicm     struct mode_tree_item *top, enum window_customize_scope scope,
27367c16a7cSnicm     struct options_entry *o, struct format_tree *ft,
27467c16a7cSnicm     const char *filter, struct cmd_find_state *fs)
27567c16a7cSnicm {
27667c16a7cSnicm 	const struct options_table_entry	*oe = options_table_entry(o);
27767c16a7cSnicm 	struct options				*oo = options_owner(o);
27867c16a7cSnicm 	const char				*name = options_name(o);
27967c16a7cSnicm 	struct window_customize_itemdata	*item;
28067c16a7cSnicm 	char					*text, *expanded, *value;
28167c16a7cSnicm 	int					 global = 0, array = 0;
28267c16a7cSnicm 	uint64_t				 tag;
28367c16a7cSnicm 
28467c16a7cSnicm 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
28567c16a7cSnicm 		return;
28667c16a7cSnicm 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY))
28767c16a7cSnicm 		array = 1;
28867c16a7cSnicm 
28967c16a7cSnicm 	if (scope == WINDOW_CUSTOMIZE_SERVER ||
29067c16a7cSnicm 	    scope == WINDOW_CUSTOMIZE_GLOBAL_SESSION ||
29167c16a7cSnicm 	    scope == WINDOW_CUSTOMIZE_GLOBAL_WINDOW)
29267c16a7cSnicm 		global = 1;
29367c16a7cSnicm 	if (data->hide_global && global)
29467c16a7cSnicm 		return;
29567c16a7cSnicm 
29667c16a7cSnicm 	format_add(ft, "option_name", "%s", name);
29767c16a7cSnicm 	format_add(ft, "option_is_global", "%d", global);
29867c16a7cSnicm 	format_add(ft, "option_is_array", "%d", array);
29967c16a7cSnicm 
30067c16a7cSnicm 	text = window_customize_scope_text(scope, fs);
30167c16a7cSnicm 	format_add(ft, "option_scope", "%s", text);
30267c16a7cSnicm 	free(text);
30367c16a7cSnicm 
30467c16a7cSnicm 	if (oe != NULL && oe->unit != NULL)
30567c16a7cSnicm 		format_add(ft, "option_unit", "%s", oe->unit);
30667c16a7cSnicm 	else
30767c16a7cSnicm 		format_add(ft, "option_unit", "%s", "");
30867c16a7cSnicm 
30967c16a7cSnicm 	if (!array) {
31067c16a7cSnicm 		value = options_to_string(o, -1, 0);
31167c16a7cSnicm 		format_add(ft, "option_value", "%s", value);
31267c16a7cSnicm 		free(value);
31367c16a7cSnicm 	}
31467c16a7cSnicm 
31567c16a7cSnicm 	if (filter != NULL) {
31667c16a7cSnicm 		expanded = format_expand(ft, filter);
31767c16a7cSnicm 		if (!format_true(expanded)) {
31867c16a7cSnicm 			free(expanded);
31967c16a7cSnicm 			return;
32067c16a7cSnicm 		}
32167c16a7cSnicm 		free(expanded);
32267c16a7cSnicm 	}
32367c16a7cSnicm 	item = window_customize_add_item(data);
32467c16a7cSnicm 	item->oo = oo;
32567c16a7cSnicm 	item->scope = scope;
32667c16a7cSnicm 	item->name = xstrdup(name);
32767c16a7cSnicm 	item->idx = -1;
32867c16a7cSnicm 
32967c16a7cSnicm 	if (array)
33067c16a7cSnicm 		text = NULL;
33167c16a7cSnicm 	else
33267c16a7cSnicm 		text = format_expand(ft, data->format);
33367c16a7cSnicm 	tag = window_customize_get_tag(o, -1, oe);
33467c16a7cSnicm 	top = mode_tree_add(data->data, top, item, tag, name, text, 0);
33567c16a7cSnicm 	free(text);
33667c16a7cSnicm 
33767c16a7cSnicm 	if (array)
33867c16a7cSnicm 		window_customize_build_array(data, top, scope, o, ft);
33967c16a7cSnicm }
34067c16a7cSnicm 
34167c16a7cSnicm static void
34267c16a7cSnicm window_customize_find_user_options(struct options *oo, const char ***list,
34367c16a7cSnicm     u_int *size)
34467c16a7cSnicm {
34567c16a7cSnicm 	struct options_entry	*o;
34667c16a7cSnicm 	const char		*name;
34767c16a7cSnicm 	u_int			 i;
34867c16a7cSnicm 
34967c16a7cSnicm 	o = options_first(oo);
35067c16a7cSnicm 	while (o != NULL) {
35167c16a7cSnicm 		name = options_name(o);
35267c16a7cSnicm 		if (*name != '@') {
35367c16a7cSnicm 			o = options_next(o);
35467c16a7cSnicm 			continue;
35567c16a7cSnicm 		}
35667c16a7cSnicm 		for (i = 0; i < *size; i++) {
35767c16a7cSnicm 			if (strcmp((*list)[i], name) == 0)
35867c16a7cSnicm 				break;
35967c16a7cSnicm 		}
36067c16a7cSnicm 		if (i != *size) {
36167c16a7cSnicm 			o = options_next(o);
36267c16a7cSnicm 			continue;
36367c16a7cSnicm 		}
36467c16a7cSnicm 		*list = xreallocarray(*list, (*size) + 1, sizeof **list);
36567c16a7cSnicm 		(*list)[(*size)++] = name;
36667c16a7cSnicm 
36767c16a7cSnicm 		o = options_next(o);
36867c16a7cSnicm 	}
36967c16a7cSnicm }
37067c16a7cSnicm 
37167c16a7cSnicm static void
37267c16a7cSnicm window_customize_build_options(struct window_customize_modedata *data,
37367c16a7cSnicm     const char *title, uint64_t tag,
37467c16a7cSnicm     enum window_customize_scope scope0, struct options *oo0,
37567c16a7cSnicm     enum window_customize_scope scope1, struct options *oo1,
37667c16a7cSnicm     enum window_customize_scope scope2, struct options *oo2,
37767c16a7cSnicm     struct format_tree *ft, const char *filter, struct cmd_find_state *fs)
37867c16a7cSnicm {
37967c16a7cSnicm 	struct mode_tree_item		 *top;
380cf415eadSnicm 	struct options_entry		 *o = NULL, *loop;
38167c16a7cSnicm 	const char			**list = NULL, *name;
38267c16a7cSnicm 	u_int				  size = 0, i;
38367c16a7cSnicm 	enum window_customize_scope	  scope;
38467c16a7cSnicm 
38567c16a7cSnicm 	top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
38694c0d63cSnicm 	mode_tree_no_tag(top);
38767c16a7cSnicm 
38867c16a7cSnicm 	/*
38967c16a7cSnicm 	 * We get the options from the first tree, but build it using the
39067c16a7cSnicm 	 * values from the other two. Any tree can have user options so we need
39167c16a7cSnicm 	 * to build a separate list of them.
39267c16a7cSnicm 	 */
39367c16a7cSnicm 
39467c16a7cSnicm 	window_customize_find_user_options(oo0, &list, &size);
39567c16a7cSnicm 	if (oo1 != NULL)
39667c16a7cSnicm 		window_customize_find_user_options(oo1, &list, &size);
39767c16a7cSnicm 	if (oo2 != NULL)
39867c16a7cSnicm 		window_customize_find_user_options(oo2, &list, &size);
39967c16a7cSnicm 
40067c16a7cSnicm 	for (i = 0; i < size; i++) {
40167c16a7cSnicm 		if (oo2 != NULL)
40295143b6eSnicm 			o = options_get(oo2, list[i]);
4035784a26aSnicm 		if (o == NULL && oo1 != NULL)
40467c16a7cSnicm 			o = options_get(oo1, list[i]);
4055784a26aSnicm 		if (o == NULL)
40695143b6eSnicm 			o = options_get(oo0, list[i]);
40767c16a7cSnicm 		if (options_owner(o) == oo2)
40867c16a7cSnicm 			scope = scope2;
40967c16a7cSnicm 		else if (options_owner(o) == oo1)
41067c16a7cSnicm 			scope = scope1;
41167c16a7cSnicm 		else
41267c16a7cSnicm 			scope = scope0;
41367c16a7cSnicm 		window_customize_build_option(data, top, scope, o, ft, filter,
41467c16a7cSnicm 		    fs);
41567c16a7cSnicm 	}
41667c16a7cSnicm 	free(list);
41767c16a7cSnicm 
41867c16a7cSnicm 	loop = options_first(oo0);
41967c16a7cSnicm 	while (loop != NULL) {
42067c16a7cSnicm 		name = options_name(loop);
42167c16a7cSnicm 		if (*name == '@') {
42267c16a7cSnicm 			loop = options_next(loop);
42367c16a7cSnicm 			continue;
42467c16a7cSnicm 		}
42567c16a7cSnicm 		if (oo2 != NULL)
42667c16a7cSnicm 			o = options_get(oo2, name);
42767c16a7cSnicm 		else if (oo1 != NULL)
42867c16a7cSnicm 			o = options_get(oo1, name);
42967c16a7cSnicm 		else
43067c16a7cSnicm 			o = loop;
43167c16a7cSnicm 		if (options_owner(o) == oo2)
43267c16a7cSnicm 			scope = scope2;
43367c16a7cSnicm 		else if (options_owner(o) == oo1)
43467c16a7cSnicm 			scope = scope1;
43567c16a7cSnicm 		else
43667c16a7cSnicm 			scope = scope0;
43767c16a7cSnicm 		window_customize_build_option(data, top, scope, o, ft, filter,
43867c16a7cSnicm 		    fs);
43967c16a7cSnicm 		loop = options_next(loop);
44067c16a7cSnicm 	}
44167c16a7cSnicm }
44267c16a7cSnicm 
44367c16a7cSnicm static void
44467c16a7cSnicm window_customize_build_keys(struct window_customize_modedata *data,
44567c16a7cSnicm     struct key_table *kt, struct format_tree *ft, const char *filter,
44667c16a7cSnicm     struct cmd_find_state *fs, u_int number)
44767c16a7cSnicm {
44867c16a7cSnicm 	struct mode_tree_item			*top, *child, *mti;
44967c16a7cSnicm 	struct window_customize_itemdata	*item;
45067c16a7cSnicm 	struct key_binding			*bd;
45167c16a7cSnicm 	char					*title, *text, *tmp, *expanded;
45267c16a7cSnicm 	const char				*flag;
45367c16a7cSnicm 	uint64_t				 tag;
45467c16a7cSnicm 
45567c16a7cSnicm 	tag = (1ULL << 62)|((uint64_t)number << 54)|1;
45667c16a7cSnicm 
45767c16a7cSnicm 	xasprintf(&title, "Key Table - %s", kt->name);
45867c16a7cSnicm 	top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
45994c0d63cSnicm 	mode_tree_no_tag(top);
46067c16a7cSnicm 	free(title);
46167c16a7cSnicm 
46267c16a7cSnicm 	ft = format_create_from_state(NULL, NULL, fs);
46367c16a7cSnicm 	format_add(ft, "is_option", "0");
46467c16a7cSnicm 	format_add(ft, "is_key", "1");
46567c16a7cSnicm 
46667c16a7cSnicm 	bd = key_bindings_first(kt);
46767c16a7cSnicm 	while (bd != NULL) {
4685416581eSnicm 		format_add(ft, "key", "%s", key_string_lookup_key(bd->key, 0));
46967c16a7cSnicm 		if (bd->note != NULL)
47067c16a7cSnicm 			format_add(ft, "key_note", "%s", bd->note);
47167c16a7cSnicm 		if (filter != NULL) {
47267c16a7cSnicm 			expanded = format_expand(ft, filter);
47367c16a7cSnicm 			if (!format_true(expanded)) {
47467c16a7cSnicm 				free(expanded);
47567c16a7cSnicm 				continue;
47667c16a7cSnicm 			}
47767c16a7cSnicm 			free(expanded);
47867c16a7cSnicm 		}
47967c16a7cSnicm 
48067c16a7cSnicm 		item = window_customize_add_item(data);
48167c16a7cSnicm 		item->scope = WINDOW_CUSTOMIZE_KEY;
48267c16a7cSnicm 		item->table = xstrdup(kt->name);
48367c16a7cSnicm 		item->key = bd->key;
48494c0d63cSnicm 		item->name = xstrdup(key_string_lookup_key(item->key, 0));
48594c0d63cSnicm 		item->idx = -1;
48667c16a7cSnicm 
48767c16a7cSnicm 		expanded = format_expand(ft, data->format);
48867c16a7cSnicm 		child = mode_tree_add(data->data, top, item, (uint64_t)bd,
48967c16a7cSnicm 		    expanded, NULL, 0);
49067c16a7cSnicm 		free(expanded);
49167c16a7cSnicm 
49267c16a7cSnicm 		tmp = cmd_list_print(bd->cmdlist, 0);
49367c16a7cSnicm 		xasprintf(&text, "#[ignore]%s", tmp);
49467c16a7cSnicm 		free(tmp);
49567c16a7cSnicm 		mti = mode_tree_add(data->data, child, item,
49667c16a7cSnicm 		    tag|(bd->key << 3)|(0 << 1)|1, "Command", text, -1);
49767c16a7cSnicm 		mode_tree_draw_as_parent(mti);
49894c0d63cSnicm 		mode_tree_no_tag(mti);
49967c16a7cSnicm 		free(text);
50067c16a7cSnicm 
50167c16a7cSnicm 		if (bd->note != NULL)
50267c16a7cSnicm 			xasprintf(&text, "#[ignore]%s", bd->note);
50367c16a7cSnicm 		else
50467c16a7cSnicm 			text = xstrdup("");
50567c16a7cSnicm 		mti = mode_tree_add(data->data, child, item,
50667c16a7cSnicm 		    tag|(bd->key << 3)|(1 << 1)|1, "Note", text, -1);
50767c16a7cSnicm 		mode_tree_draw_as_parent(mti);
50894c0d63cSnicm 		mode_tree_no_tag(mti);
50967c16a7cSnicm 		free(text);
51067c16a7cSnicm 
51167c16a7cSnicm 		if (bd->flags & KEY_BINDING_REPEAT)
51267c16a7cSnicm 			flag = "on";
51367c16a7cSnicm 		else
51467c16a7cSnicm 			flag = "off";
51567c16a7cSnicm 		mti = mode_tree_add(data->data, child, item,
51667c16a7cSnicm 		    tag|(bd->key << 3)|(2 << 1)|1, "Repeat", flag, -1);
51767c16a7cSnicm 		mode_tree_draw_as_parent(mti);
51894c0d63cSnicm 		mode_tree_no_tag(mti);
51967c16a7cSnicm 
52067c16a7cSnicm 		bd = key_bindings_next(kt, bd);
52167c16a7cSnicm 	}
52267c16a7cSnicm 
52367c16a7cSnicm 	format_free(ft);
52467c16a7cSnicm }
52567c16a7cSnicm 
52667c16a7cSnicm static void
52767c16a7cSnicm window_customize_build(void *modedata,
52867c16a7cSnicm     __unused struct mode_tree_sort_criteria *sort_crit, __unused uint64_t *tag,
52967c16a7cSnicm     const char *filter)
53067c16a7cSnicm {
53167c16a7cSnicm 	struct window_customize_modedata	*data = modedata;
53267c16a7cSnicm 	struct cmd_find_state			 fs;
53367c16a7cSnicm 	struct format_tree			*ft;
53467c16a7cSnicm 	u_int					 i;
53567c16a7cSnicm 	struct key_table			*kt;
53667c16a7cSnicm 
53767c16a7cSnicm 	for (i = 0; i < data->item_size; i++)
53867c16a7cSnicm 		window_customize_free_item(data->item_list[i]);
53967c16a7cSnicm 	free(data->item_list);
54067c16a7cSnicm 	data->item_list = NULL;
54167c16a7cSnicm 	data->item_size = 0;
54267c16a7cSnicm 
54367c16a7cSnicm 	if (cmd_find_valid_state(&data->fs))
54467c16a7cSnicm 		cmd_find_copy_state(&fs, &data->fs);
54567c16a7cSnicm 	else
54667c16a7cSnicm 		cmd_find_from_pane(&fs, data->wp, 0);
54767c16a7cSnicm 
54867c16a7cSnicm 	ft = format_create_from_state(NULL, NULL, &fs);
54967c16a7cSnicm 	format_add(ft, "is_option", "1");
55067c16a7cSnicm 	format_add(ft, "is_key", "0");
55167c16a7cSnicm 
55267c16a7cSnicm 	window_customize_build_options(data, "Server Options",
55367c16a7cSnicm 	    (3ULL << 62)|(OPTIONS_TABLE_SERVER << 1)|1,
55467c16a7cSnicm 	    WINDOW_CUSTOMIZE_SERVER, global_options,
55567c16a7cSnicm 	    WINDOW_CUSTOMIZE_NONE, NULL,
55667c16a7cSnicm 	    WINDOW_CUSTOMIZE_NONE, NULL,
55767c16a7cSnicm 	    ft, filter, &fs);
55867c16a7cSnicm 	window_customize_build_options(data, "Session Options",
55967c16a7cSnicm 	    (3ULL << 62)|(OPTIONS_TABLE_SESSION << 1)|1,
56067c16a7cSnicm 	    WINDOW_CUSTOMIZE_GLOBAL_SESSION, global_s_options,
56167c16a7cSnicm 	    WINDOW_CUSTOMIZE_SESSION, fs.s->options,
56267c16a7cSnicm 	    WINDOW_CUSTOMIZE_NONE, NULL,
56367c16a7cSnicm 	    ft, filter, &fs);
56467c16a7cSnicm 	window_customize_build_options(data, "Window & Pane Options",
56567c16a7cSnicm 	    (3ULL << 62)|(OPTIONS_TABLE_WINDOW << 1)|1,
56667c16a7cSnicm 	    WINDOW_CUSTOMIZE_GLOBAL_WINDOW, global_w_options,
56767c16a7cSnicm 	    WINDOW_CUSTOMIZE_WINDOW, fs.w->options,
56867c16a7cSnicm 	    WINDOW_CUSTOMIZE_PANE, fs.wp->options,
56967c16a7cSnicm 	    ft, filter, &fs);
57067c16a7cSnicm 
57167c16a7cSnicm 	format_free(ft);
57267c16a7cSnicm 	ft = format_create_from_state(NULL, NULL, &fs);
57367c16a7cSnicm 
57467c16a7cSnicm 	i = 0;
57567c16a7cSnicm 	kt = key_bindings_first_table();
57667c16a7cSnicm 	while (kt != NULL) {
57767c16a7cSnicm 		if (!RB_EMPTY(&kt->key_bindings)) {
57867c16a7cSnicm 			window_customize_build_keys(data, kt, ft, filter, &fs,
57967c16a7cSnicm 			    i);
58067c16a7cSnicm 			if (++i == 256)
58167c16a7cSnicm 				break;
58267c16a7cSnicm 		}
58367c16a7cSnicm 		kt = key_bindings_next_table(kt);
58467c16a7cSnicm 	}
58567c16a7cSnicm 
58667c16a7cSnicm 	format_free(ft);
58767c16a7cSnicm }
58867c16a7cSnicm 
58967c16a7cSnicm static void
59067c16a7cSnicm window_customize_draw_key(__unused struct window_customize_modedata *data,
59167c16a7cSnicm     struct window_customize_itemdata *item, struct screen_write_ctx *ctx,
59267c16a7cSnicm     u_int sx, u_int sy)
59367c16a7cSnicm {
59467c16a7cSnicm 	struct screen		*s = ctx->s;
59567c16a7cSnicm 	u_int			 cx = s->cx, cy = s->cy;
59667c16a7cSnicm 	struct key_table	*kt;
59767c16a7cSnicm 	struct key_binding	*bd, *default_bd;
59867c16a7cSnicm 	const char		*note, *period = "";
59967c16a7cSnicm 	char			*cmd, *default_cmd;
60067c16a7cSnicm 
60167c16a7cSnicm 	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
60267c16a7cSnicm 		return;
60367c16a7cSnicm 
60467c16a7cSnicm 	note = bd->note;
60567c16a7cSnicm 	if (note == NULL)
60667c16a7cSnicm 		note = "There is no note for this key.";
60767c16a7cSnicm 	if (*note != '\0' && note[strlen (note) - 1] != '.')
60867c16a7cSnicm 		period = ".";
60967c16a7cSnicm 	if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s%s",
61067c16a7cSnicm 	    note, period))
61167c16a7cSnicm 		return;
61267c16a7cSnicm 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
61367c16a7cSnicm 	if (s->cy >= cy + sy - 1)
61467c16a7cSnicm 		return;
61567c16a7cSnicm 
61667c16a7cSnicm 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
61767c16a7cSnicm 	    &grid_default_cell, "This key is in the %s table.", kt->name))
61867c16a7cSnicm 		return;
61967c16a7cSnicm 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
62067c16a7cSnicm 	    &grid_default_cell, "This key %s repeat.",
62167c16a7cSnicm 	    (bd->flags & KEY_BINDING_REPEAT) ? "does" : "does not"))
62267c16a7cSnicm 		return;
62367c16a7cSnicm 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
62467c16a7cSnicm 	if (s->cy >= cy + sy - 1)
62567c16a7cSnicm 		return;
62667c16a7cSnicm 
62767c16a7cSnicm 	cmd = cmd_list_print(bd->cmdlist, 0);
62867c16a7cSnicm 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
62967c16a7cSnicm 	    &grid_default_cell, "Command: %s", cmd)) {
63067c16a7cSnicm 		free(cmd);
63167c16a7cSnicm 		return;
63267c16a7cSnicm 	}
63367c16a7cSnicm 	default_bd = key_bindings_get_default(kt, bd->key);
63467c16a7cSnicm 	if (default_bd != NULL) {
63567c16a7cSnicm 		default_cmd = cmd_list_print(default_bd->cmdlist, 0);
63667c16a7cSnicm 		if (strcmp(cmd, default_cmd) != 0 &&
63767c16a7cSnicm 		    !screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
63867c16a7cSnicm 		    &grid_default_cell, "The default is: %s", default_cmd)) {
63967c16a7cSnicm 			free(default_cmd);
64067c16a7cSnicm 			free(cmd);
64167c16a7cSnicm 			return;
64267c16a7cSnicm 		}
64367c16a7cSnicm 		free(default_cmd);
64467c16a7cSnicm 	}
64567c16a7cSnicm 	free(cmd);
64667c16a7cSnicm }
64767c16a7cSnicm 
64867c16a7cSnicm static void
64967c16a7cSnicm window_customize_draw_option(struct window_customize_modedata *data,
65067c16a7cSnicm     struct window_customize_itemdata *item, struct screen_write_ctx *ctx,
65167c16a7cSnicm     u_int sx, u_int sy)
65267c16a7cSnicm {
65367c16a7cSnicm 	struct screen				 *s = ctx->s;
65467c16a7cSnicm 	u_int					  cx = s->cx, cy = s->cy;
65567c16a7cSnicm 	int					  idx;
65667c16a7cSnicm 	struct options_entry			 *o, *parent;
65767c16a7cSnicm 	struct options				 *go, *wo;
65867c16a7cSnicm 	const struct options_table_entry	 *oe;
65967c16a7cSnicm 	struct grid_cell			  gc;
66067c16a7cSnicm 	const char				**choice, *text, *name;
66167c16a7cSnicm 	const char				 *space = "", *unit = "";
66267c16a7cSnicm 	char					 *value = NULL, *expanded;
66367c16a7cSnicm 	char					 *default_value = NULL;
66467c16a7cSnicm 	char					  choices[256] = "";
66567c16a7cSnicm 	struct cmd_find_state			  fs;
66667c16a7cSnicm 	struct format_tree			 *ft;
66767c16a7cSnicm 
66867c16a7cSnicm 	if (!window_customize_check_item(data, item, &fs))
66967c16a7cSnicm 		return;
67067c16a7cSnicm 	name = item->name;
67167c16a7cSnicm 	idx = item->idx;
67267c16a7cSnicm 
67367c16a7cSnicm 	o = options_get(item->oo, name);
67467c16a7cSnicm 	if (o == NULL)
67567c16a7cSnicm 		return;
67667c16a7cSnicm 	oe = options_table_entry(o);
67767c16a7cSnicm 
67867c16a7cSnicm 	if (oe != NULL && oe->unit != NULL) {
67967c16a7cSnicm 		space = " ";
68067c16a7cSnicm 		unit = oe->unit;
68167c16a7cSnicm 	}
68267c16a7cSnicm 	ft = format_create_from_state(NULL, NULL, &fs);
68367c16a7cSnicm 
6847320143cSnicm 	if (oe == NULL || oe->text == NULL)
68567c16a7cSnicm 		text = "This option doesn't have a description.";
68667c16a7cSnicm 	else
68767c16a7cSnicm 		text = oe->text;
68867c16a7cSnicm 	if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s",
68967c16a7cSnicm 	    text))
69067c16a7cSnicm 		goto out;
69167c16a7cSnicm 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
69267c16a7cSnicm 	if (s->cy >= cy + sy - 1)
69367c16a7cSnicm 		goto out;
69467c16a7cSnicm 
69567c16a7cSnicm 	if (oe == NULL)
69667c16a7cSnicm 		text = "user";
69767c16a7cSnicm 	else if ((oe->scope & (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) ==
69867c16a7cSnicm 	    (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE))
69967c16a7cSnicm 		text = "window and pane";
70067c16a7cSnicm 	else if (oe->scope & OPTIONS_TABLE_WINDOW)
70167c16a7cSnicm 		text = "window";
70267c16a7cSnicm 	else if (oe->scope & OPTIONS_TABLE_SESSION)
70367c16a7cSnicm 		text = "session";
70467c16a7cSnicm 	else
70567c16a7cSnicm 		text = "server";
70667c16a7cSnicm 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
70767c16a7cSnicm 	    &grid_default_cell, "This is a %s option.", text))
70867c16a7cSnicm 		goto out;
70967c16a7cSnicm 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
71067c16a7cSnicm 		if (idx != -1) {
71167c16a7cSnicm 			if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
71267c16a7cSnicm 			    0, &grid_default_cell,
71367c16a7cSnicm 			    "This is an array option, index %u.", idx))
71467c16a7cSnicm 				goto out;
71567c16a7cSnicm 		} else {
71667c16a7cSnicm 			if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
71767c16a7cSnicm 			    0, &grid_default_cell, "This is an array option."))
71867c16a7cSnicm 				goto out;
71967c16a7cSnicm 		}
72067c16a7cSnicm 		if (idx == -1)
72167c16a7cSnicm 			goto out;
72267c16a7cSnicm 	}
72367c16a7cSnicm 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
72467c16a7cSnicm 	if (s->cy >= cy + sy - 1)
72567c16a7cSnicm 		goto out;
72667c16a7cSnicm 
72767c16a7cSnicm 	value = options_to_string(o, idx, 0);
72867c16a7cSnicm 	if (oe != NULL && idx == -1) {
72967c16a7cSnicm 		default_value = options_default_to_string(oe);
73067c16a7cSnicm 		if (strcmp(default_value, value) == 0) {
73167c16a7cSnicm 			free(default_value);
73267c16a7cSnicm 			default_value = NULL;
73367c16a7cSnicm 		}
73467c16a7cSnicm 	}
73567c16a7cSnicm 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
73667c16a7cSnicm 	    &grid_default_cell, "Option value: %s%s%s", value, space, unit))
73767c16a7cSnicm 		goto out;
73867c16a7cSnicm 	if (oe == NULL || oe->type == OPTIONS_TABLE_STRING) {
73967c16a7cSnicm 		expanded = format_expand(ft, value);
74067c16a7cSnicm 		if (strcmp(expanded, value) != 0) {
74167c16a7cSnicm 			if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
74267c16a7cSnicm 			    0, &grid_default_cell, "This expands to: %s",
74367c16a7cSnicm 			    expanded))
74467c16a7cSnicm 				goto out;
74567c16a7cSnicm 		}
74667c16a7cSnicm 		free(expanded);
74767c16a7cSnicm 	}
74867c16a7cSnicm 	if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) {
74967c16a7cSnicm 		for (choice = oe->choices; *choice != NULL; choice++) {
75067c16a7cSnicm 			strlcat(choices, *choice, sizeof choices);
75167c16a7cSnicm 			strlcat(choices, ", ", sizeof choices);
75267c16a7cSnicm 		}
75367c16a7cSnicm 		choices[strlen(choices) - 2] = '\0';
75467c16a7cSnicm 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
75567c16a7cSnicm 		    &grid_default_cell, "Available values are: %s",
75667c16a7cSnicm 		    choices))
75767c16a7cSnicm 			goto out;
75867c16a7cSnicm 	}
75967c16a7cSnicm 	if (oe != NULL && oe->type == OPTIONS_TABLE_COLOUR) {
76067c16a7cSnicm 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
76167c16a7cSnicm 		    &grid_default_cell, "This is a colour option: "))
76267c16a7cSnicm 			goto out;
76367c16a7cSnicm 		memcpy(&gc, &grid_default_cell, sizeof gc);
76467c16a7cSnicm 		gc.fg = options_get_number(item->oo, name);
76567c16a7cSnicm 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
76667c16a7cSnicm 		    "EXAMPLE"))
76767c16a7cSnicm 			goto out;
76867c16a7cSnicm 	}
76967c16a7cSnicm 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_STYLE)) {
77067c16a7cSnicm 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
77167c16a7cSnicm 		    &grid_default_cell, "This is a style option: "))
77267c16a7cSnicm 			goto out;
77367c16a7cSnicm 		style_apply(&gc, item->oo, name, ft);
77467c16a7cSnicm 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
77567c16a7cSnicm 		    "EXAMPLE"))
77667c16a7cSnicm 			goto out;
77767c16a7cSnicm 	}
77867c16a7cSnicm 	if (default_value != NULL) {
77967c16a7cSnicm 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
78067c16a7cSnicm 		    &grid_default_cell, "The default is: %s%s%s", default_value,
78167c16a7cSnicm 		    space, unit))
78267c16a7cSnicm 			goto out;
78367c16a7cSnicm 	}
78467c16a7cSnicm 
78567c16a7cSnicm 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
78667c16a7cSnicm 	if (s->cy > cy + sy - 1)
78767c16a7cSnicm 		goto out;
78867c16a7cSnicm 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
78967c16a7cSnicm 		wo = NULL;
79067c16a7cSnicm 		go = NULL;
79167c16a7cSnicm 	} else {
79267c16a7cSnicm 		switch (item->scope) {
79367c16a7cSnicm 		case WINDOW_CUSTOMIZE_PANE:
79467c16a7cSnicm 			wo = options_get_parent(item->oo);
79567c16a7cSnicm 			go = options_get_parent(wo);
79667c16a7cSnicm 			break;
79767c16a7cSnicm 		case WINDOW_CUSTOMIZE_WINDOW:
79867c16a7cSnicm 		case WINDOW_CUSTOMIZE_SESSION:
79967c16a7cSnicm 			wo = NULL;
80067c16a7cSnicm 			go = options_get_parent(item->oo);
80167c16a7cSnicm 			break;
80267c16a7cSnicm 		default:
80367c16a7cSnicm 			wo = NULL;
80467c16a7cSnicm 			go = NULL;
80567c16a7cSnicm 			break;
80667c16a7cSnicm 		}
80767c16a7cSnicm 	}
80867c16a7cSnicm 	if (wo != NULL && options_owner(o) != wo) {
80967c16a7cSnicm 		parent = options_get_only(wo, name);
81067c16a7cSnicm 		if (parent != NULL) {
81167c16a7cSnicm 			value = options_to_string(parent, -1 , 0);
81267c16a7cSnicm 			if (!screen_write_text(ctx, s->cx, sx,
81367c16a7cSnicm 			    sy - (s->cy - cy), 0, &grid_default_cell,
81467c16a7cSnicm 			    "Window value (from window %u): %s%s%s", fs.wl->idx,
81567c16a7cSnicm 			    value, space, unit))
81667c16a7cSnicm 				goto out;
81767c16a7cSnicm 		}
81867c16a7cSnicm 	}
81967c16a7cSnicm 	if (go != NULL && options_owner(o) != go) {
82067c16a7cSnicm 		parent = options_get_only(go, name);
82167c16a7cSnicm 		if (parent != NULL) {
82267c16a7cSnicm 			value = options_to_string(parent, -1 , 0);
82367c16a7cSnicm 			if (!screen_write_text(ctx, s->cx, sx,
82467c16a7cSnicm 			    sy - (s->cy - cy), 0, &grid_default_cell,
82567c16a7cSnicm 			    "Global value: %s%s%s", value, space, unit))
82667c16a7cSnicm 				goto out;
82767c16a7cSnicm 		}
82867c16a7cSnicm 	}
82967c16a7cSnicm 
83067c16a7cSnicm out:
83167c16a7cSnicm 	free(value);
83267c16a7cSnicm 	free(default_value);
83367c16a7cSnicm 	format_free(ft);
83467c16a7cSnicm }
83567c16a7cSnicm 
83667c16a7cSnicm static void
83767c16a7cSnicm window_customize_draw(void *modedata, void *itemdata,
83867c16a7cSnicm     struct screen_write_ctx *ctx, u_int sx, u_int sy)
83967c16a7cSnicm {
84067c16a7cSnicm 	struct window_customize_modedata	*data = modedata;
84167c16a7cSnicm 	struct window_customize_itemdata	*item = itemdata;
84267c16a7cSnicm 
84367c16a7cSnicm 	if (item == NULL)
84467c16a7cSnicm 		return;
84567c16a7cSnicm 
84667c16a7cSnicm 	if (item->scope == WINDOW_CUSTOMIZE_KEY)
84767c16a7cSnicm 		window_customize_draw_key(data, item, ctx, sx, sy);
84867c16a7cSnicm 	else
84967c16a7cSnicm 		window_customize_draw_option(data, item, ctx, sx, sy);
85067c16a7cSnicm }
85167c16a7cSnicm 
85267c16a7cSnicm static void
85367c16a7cSnicm window_customize_menu(void *modedata, struct client *c, key_code key)
85467c16a7cSnicm {
85567c16a7cSnicm 	struct window_customize_modedata	*data = modedata;
85667c16a7cSnicm 	struct window_pane			*wp = data->wp;
85767c16a7cSnicm 	struct window_mode_entry		*wme;
85867c16a7cSnicm 
85967c16a7cSnicm 	wme = TAILQ_FIRST(&wp->modes);
86067c16a7cSnicm 	if (wme == NULL || wme->data != modedata)
86167c16a7cSnicm 		return;
86267c16a7cSnicm 	window_customize_key(wme, c, NULL, NULL, key, NULL);
86367c16a7cSnicm }
86467c16a7cSnicm 
86567c16a7cSnicm static u_int
86667c16a7cSnicm window_customize_height(__unused void *modedata, __unused u_int height)
86767c16a7cSnicm {
86867c16a7cSnicm 	return (12);
86967c16a7cSnicm }
87067c16a7cSnicm 
87167c16a7cSnicm static struct screen *
87267c16a7cSnicm window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
87367c16a7cSnicm     struct args *args)
87467c16a7cSnicm {
87567c16a7cSnicm 	struct window_pane			*wp = wme->wp;
87667c16a7cSnicm 	struct window_customize_modedata	*data;
87767c16a7cSnicm 	struct screen				*s;
87867c16a7cSnicm 
87967c16a7cSnicm 	wme->data = data = xcalloc(1, sizeof *data);
88067c16a7cSnicm 	data->wp = wp;
88167c16a7cSnicm 	data->references = 1;
88267c16a7cSnicm 
88367c16a7cSnicm 	memcpy(&data->fs, fs, sizeof data->fs);
88467c16a7cSnicm 
88567c16a7cSnicm 	if (args == NULL || !args_has(args, 'F'))
88667c16a7cSnicm 		data->format = xstrdup(WINDOW_CUSTOMIZE_DEFAULT_FORMAT);
88767c16a7cSnicm 	else
88867c16a7cSnicm 		data->format = xstrdup(args_get(args, 'F'));
889*af11b61dSnicm 	if (args_has(args, 'y'))
890*af11b61dSnicm 		data->prompt_flags = PROMPT_ACCEPT;
89167c16a7cSnicm 
89267c16a7cSnicm 	data->data = mode_tree_start(wp, args, window_customize_build,
89367c16a7cSnicm 	    window_customize_draw, NULL, window_customize_menu,
894438eed14Snicm 	    window_customize_height, NULL, data, window_customize_menu_items,
895438eed14Snicm 	    NULL, 0, &s);
89667c16a7cSnicm 	mode_tree_zoom(data->data, args);
89767c16a7cSnicm 
89867c16a7cSnicm 	mode_tree_build(data->data);
89967c16a7cSnicm 	mode_tree_draw(data->data);
90067c16a7cSnicm 
90167c16a7cSnicm 	return (s);
90267c16a7cSnicm }
90367c16a7cSnicm 
90467c16a7cSnicm static void
90567c16a7cSnicm window_customize_destroy(struct window_customize_modedata *data)
90667c16a7cSnicm {
90767c16a7cSnicm 	u_int	i;
90867c16a7cSnicm 
90967c16a7cSnicm 	if (--data->references != 0)
91067c16a7cSnicm 		return;
91167c16a7cSnicm 
91267c16a7cSnicm 	for (i = 0; i < data->item_size; i++)
91367c16a7cSnicm 		window_customize_free_item(data->item_list[i]);
91467c16a7cSnicm 	free(data->item_list);
91567c16a7cSnicm 
91667c16a7cSnicm 	free(data->format);
91767c16a7cSnicm 
91867c16a7cSnicm 	free(data);
91967c16a7cSnicm }
92067c16a7cSnicm 
92167c16a7cSnicm static void
92267c16a7cSnicm window_customize_free(struct window_mode_entry *wme)
92367c16a7cSnicm {
92467c16a7cSnicm 	struct window_customize_modedata *data = wme->data;
92567c16a7cSnicm 
92667c16a7cSnicm 	if (data == NULL)
92767c16a7cSnicm 		return;
92867c16a7cSnicm 
92967c16a7cSnicm 	data->dead = 1;
93067c16a7cSnicm 	mode_tree_free(data->data);
93167c16a7cSnicm 	window_customize_destroy(data);
93267c16a7cSnicm }
93367c16a7cSnicm 
93467c16a7cSnicm static void
93567c16a7cSnicm window_customize_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
93667c16a7cSnicm {
93767c16a7cSnicm 	struct window_customize_modedata	*data = wme->data;
93867c16a7cSnicm 
93967c16a7cSnicm 	mode_tree_resize(data->data, sx, sy);
94067c16a7cSnicm }
94167c16a7cSnicm 
94267c16a7cSnicm static void
94367c16a7cSnicm window_customize_free_callback(void *modedata)
94467c16a7cSnicm {
94567c16a7cSnicm 	window_customize_destroy(modedata);
94667c16a7cSnicm }
94767c16a7cSnicm 
94867c16a7cSnicm static void
94967c16a7cSnicm window_customize_free_item_callback(void *itemdata)
95067c16a7cSnicm {
95167c16a7cSnicm 	struct window_customize_itemdata	*item = itemdata;
95267c16a7cSnicm 	struct window_customize_modedata	*data = item->data;
95367c16a7cSnicm 
95467c16a7cSnicm 	window_customize_free_item(item);
95567c16a7cSnicm 	window_customize_destroy(data);
95667c16a7cSnicm }
95767c16a7cSnicm 
95867c16a7cSnicm static int
95967c16a7cSnicm window_customize_set_option_callback(struct client *c, void *itemdata,
96067c16a7cSnicm     const char *s, __unused int done)
96167c16a7cSnicm {
96267c16a7cSnicm 	struct window_customize_itemdata	*item = itemdata;
96367c16a7cSnicm 	struct window_customize_modedata	*data = item->data;
96467c16a7cSnicm 	struct options_entry			*o;
96567c16a7cSnicm 	const struct options_table_entry	*oe;
96667c16a7cSnicm 	struct options				*oo = item->oo;
96767c16a7cSnicm 	const char				*name = item->name;
96867c16a7cSnicm 	char					*cause;
96967c16a7cSnicm 	int					 idx = item->idx;
97067c16a7cSnicm 
97167c16a7cSnicm 	if (s == NULL || *s == '\0' || data->dead)
97267c16a7cSnicm 		return (0);
97367c16a7cSnicm 	if (item == NULL || !window_customize_check_item(data, item, NULL))
97467c16a7cSnicm 		return (0);
97567c16a7cSnicm 	o = options_get(oo, name);
97667c16a7cSnicm 	if (o == NULL)
97767c16a7cSnicm 		return (0);
97867c16a7cSnicm 	oe = options_table_entry(o);
97967c16a7cSnicm 
98067c16a7cSnicm 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
98167c16a7cSnicm 		if (idx == -1) {
98267c16a7cSnicm 			for (idx = 0; idx < INT_MAX; idx++) {
98367c16a7cSnicm 				if (options_array_get(o, idx) == NULL)
98467c16a7cSnicm 					break;
98567c16a7cSnicm 			}
98667c16a7cSnicm 		}
98767c16a7cSnicm 		if (options_array_set(o, idx, s, 0, &cause) != 0)
98867c16a7cSnicm 			goto fail;
98967c16a7cSnicm 	} else {
99067c16a7cSnicm 		if (options_from_string(oo, oe, name, s, 0, &cause) != 0)
99167c16a7cSnicm 			goto fail;
99267c16a7cSnicm 	}
99367c16a7cSnicm 
99467c16a7cSnicm 	options_push_changes(item->name);
99567c16a7cSnicm 	mode_tree_build(data->data);
99667c16a7cSnicm 	mode_tree_draw(data->data);
99767c16a7cSnicm 	data->wp->flags |= PANE_REDRAW;
99867c16a7cSnicm 
99967c16a7cSnicm 	return (0);
100067c16a7cSnicm 
100167c16a7cSnicm fail:
100267c16a7cSnicm 	*cause = toupper((u_char)*cause);
1003e7e79d0aSnicm 	status_message_set(c, -1, 1, 0, "%s", cause);
100467c16a7cSnicm 	free(cause);
100567c16a7cSnicm 	return (0);
100667c16a7cSnicm }
100767c16a7cSnicm 
100867c16a7cSnicm static void
100967c16a7cSnicm window_customize_set_option(struct client *c,
101067c16a7cSnicm     struct window_customize_modedata *data,
101167c16a7cSnicm     struct window_customize_itemdata *item, int global, int pane)
101267c16a7cSnicm {
101367c16a7cSnicm 	struct options_entry			*o;
101467c16a7cSnicm 	const struct options_table_entry	*oe;
101567c16a7cSnicm 	struct options				*oo;
101667c16a7cSnicm 	struct window_customize_itemdata	*new_item;
101767c16a7cSnicm 	int					 flag, idx = item->idx;
1018cf415eadSnicm 	enum window_customize_scope		 scope = WINDOW_CUSTOMIZE_NONE;
101967c16a7cSnicm 	u_int					 choice;
102067c16a7cSnicm 	const char				*name = item->name, *space = "";
102167c16a7cSnicm 	char					*prompt, *value, *text;
102267c16a7cSnicm 	struct cmd_find_state			 fs;
102367c16a7cSnicm 
102467c16a7cSnicm 	if (item == NULL || !window_customize_check_item(data, item, &fs))
102567c16a7cSnicm 		return;
102667c16a7cSnicm 	o = options_get(item->oo, name);
102767c16a7cSnicm 	if (o == NULL)
102867c16a7cSnicm 		return;
102967c16a7cSnicm 
103067c16a7cSnicm 	oe = options_table_entry(o);
1031cf415eadSnicm 	if (oe != NULL && ~oe->scope & OPTIONS_TABLE_PANE)
103267c16a7cSnicm 		pane = 0;
103367c16a7cSnicm 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
103467c16a7cSnicm 		scope = item->scope;
103567c16a7cSnicm 		oo = item->oo;
103667c16a7cSnicm 	} else {
103767c16a7cSnicm 		if (global) {
103867c16a7cSnicm 			switch (item->scope) {
103967c16a7cSnicm 			case WINDOW_CUSTOMIZE_NONE:
104067c16a7cSnicm 			case WINDOW_CUSTOMIZE_KEY:
104167c16a7cSnicm 			case WINDOW_CUSTOMIZE_SERVER:
104267c16a7cSnicm 			case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
104367c16a7cSnicm 			case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
104467c16a7cSnicm 				scope = item->scope;
104567c16a7cSnicm 				break;
104667c16a7cSnicm 			case WINDOW_CUSTOMIZE_SESSION:
104767c16a7cSnicm 				scope = WINDOW_CUSTOMIZE_GLOBAL_SESSION;
104867c16a7cSnicm 				break;
104967c16a7cSnicm 			case WINDOW_CUSTOMIZE_WINDOW:
105067c16a7cSnicm 			case WINDOW_CUSTOMIZE_PANE:
105167c16a7cSnicm 				scope = WINDOW_CUSTOMIZE_GLOBAL_WINDOW;
105267c16a7cSnicm 				break;
105367c16a7cSnicm 			}
105467c16a7cSnicm 		} else {
105567c16a7cSnicm 			switch (item->scope) {
105667c16a7cSnicm 			case WINDOW_CUSTOMIZE_NONE:
105767c16a7cSnicm 			case WINDOW_CUSTOMIZE_KEY:
105867c16a7cSnicm 			case WINDOW_CUSTOMIZE_SERVER:
105967c16a7cSnicm 			case WINDOW_CUSTOMIZE_SESSION:
106067c16a7cSnicm 				scope = item->scope;
106167c16a7cSnicm 				break;
106267c16a7cSnicm 			case WINDOW_CUSTOMIZE_WINDOW:
106367c16a7cSnicm 			case WINDOW_CUSTOMIZE_PANE:
106467c16a7cSnicm 				if (pane)
106567c16a7cSnicm 					scope = WINDOW_CUSTOMIZE_PANE;
106667c16a7cSnicm 				else
106767c16a7cSnicm 					scope = WINDOW_CUSTOMIZE_WINDOW;
106867c16a7cSnicm 				break;
106967c16a7cSnicm 			case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
107067c16a7cSnicm 				scope = WINDOW_CUSTOMIZE_SESSION;
107167c16a7cSnicm 				break;
107267c16a7cSnicm 			case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
107367c16a7cSnicm 				if (pane)
107467c16a7cSnicm 					scope = WINDOW_CUSTOMIZE_PANE;
107567c16a7cSnicm 				else
107667c16a7cSnicm 					scope = WINDOW_CUSTOMIZE_WINDOW;
107767c16a7cSnicm 				break;
107867c16a7cSnicm 			}
107967c16a7cSnicm 		}
108067c16a7cSnicm 		if (scope == item->scope)
108167c16a7cSnicm 			oo = item->oo;
108267c16a7cSnicm 		else
108367c16a7cSnicm 			oo = window_customize_get_tree(scope, &fs);
108467c16a7cSnicm 	}
108567c16a7cSnicm 
108667c16a7cSnicm 	if (oe != NULL && oe->type == OPTIONS_TABLE_FLAG) {
108767c16a7cSnicm 		flag = options_get_number(oo, name);
108867c16a7cSnicm 		options_set_number(oo, name, !flag);
108967c16a7cSnicm 	} else if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) {
109067c16a7cSnicm 		choice = options_get_number(oo, name);
109167c16a7cSnicm 		if (oe->choices[choice + 1] == NULL)
109267c16a7cSnicm 			choice = 0;
109367c16a7cSnicm 		else
109467c16a7cSnicm 			choice++;
109567c16a7cSnicm 		options_set_number(oo, name, choice);
109667c16a7cSnicm 	} else {
109767c16a7cSnicm 		text = window_customize_scope_text(scope, &fs);
109867c16a7cSnicm 		if (*text != '\0')
109967c16a7cSnicm 			space = ", for ";
110067c16a7cSnicm 		else if (scope != WINDOW_CUSTOMIZE_SERVER)
110167c16a7cSnicm 			space = ", global";
110267c16a7cSnicm 		if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
110367c16a7cSnicm 			if (idx == -1) {
110467c16a7cSnicm 				xasprintf(&prompt, "(%s[+]%s%s) ", name, space,
110567c16a7cSnicm 				    text);
110667c16a7cSnicm 			} else {
110767c16a7cSnicm 				xasprintf(&prompt, "(%s[%d]%s%s) ", name, idx,
110867c16a7cSnicm 				    space, text);
110967c16a7cSnicm 			}
111067c16a7cSnicm 		} else
111167c16a7cSnicm 			xasprintf(&prompt, "(%s%s%s) ", name, space, text);
111267c16a7cSnicm 		free(text);
111367c16a7cSnicm 
111467c16a7cSnicm 		value = options_to_string(o, idx, 0);
111567c16a7cSnicm 
111667c16a7cSnicm 		new_item = xcalloc(1, sizeof *new_item);
111767c16a7cSnicm 		new_item->data = data;
111867c16a7cSnicm 		new_item->scope = scope;
111967c16a7cSnicm 		new_item->oo = oo;
112067c16a7cSnicm 		new_item->name = xstrdup(name);
112167c16a7cSnicm 		new_item->idx = idx;
112267c16a7cSnicm 
112367c16a7cSnicm 		data->references++;
112494adf770Snicm 		status_prompt_set(c, NULL, prompt, value,
112567c16a7cSnicm 		    window_customize_set_option_callback,
112667c16a7cSnicm 		    window_customize_free_item_callback, new_item,
1127bc5a8fc2Snicm 		    PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
112867c16a7cSnicm 
112967c16a7cSnicm 		free(prompt);
113067c16a7cSnicm 		free(value);
113167c16a7cSnicm 	}
113267c16a7cSnicm }
113367c16a7cSnicm 
113467c16a7cSnicm static void
113567c16a7cSnicm window_customize_unset_option(struct window_customize_modedata *data,
113667c16a7cSnicm     struct window_customize_itemdata *item)
113767c16a7cSnicm {
113867c16a7cSnicm 	struct options_entry	*o;
113967c16a7cSnicm 
114067c16a7cSnicm 	if (item == NULL || !window_customize_check_item(data, item, NULL))
114167c16a7cSnicm 		return;
114267c16a7cSnicm 
114367c16a7cSnicm 	o = options_get(item->oo, item->name);
114467c16a7cSnicm 	if (o == NULL)
114567c16a7cSnicm 		return;
114694c0d63cSnicm 	if (item->idx != -1 && item == mode_tree_get_current(data->data))
114767c16a7cSnicm 		mode_tree_up(data->data, 0);
114894c0d63cSnicm 	options_remove_or_default(o, item->idx, NULL);
114967c16a7cSnicm }
115094c0d63cSnicm 
115194c0d63cSnicm static void
115294c0d63cSnicm window_customize_reset_option(struct window_customize_modedata *data,
115394c0d63cSnicm     struct window_customize_itemdata *item)
115494c0d63cSnicm {
115594c0d63cSnicm 	struct options		*oo;
115694c0d63cSnicm 	struct options_entry	*o;
115794c0d63cSnicm 
115894c0d63cSnicm 	if (item == NULL || !window_customize_check_item(data, item, NULL))
115994c0d63cSnicm 		return;
116094c0d63cSnicm 	if (item->idx != -1)
116194c0d63cSnicm 		return;
116294c0d63cSnicm 
116394c0d63cSnicm 	oo = item->oo;
116494c0d63cSnicm 	while (oo != NULL) {
116594c0d63cSnicm 		o = options_get_only(item->oo, item->name);
116694c0d63cSnicm 		if (o != NULL)
116794c0d63cSnicm 			options_remove_or_default(o, -1, NULL);
116894c0d63cSnicm 		oo = options_get_parent(oo);
116994c0d63cSnicm 	}
117067c16a7cSnicm }
117167c16a7cSnicm 
117267c16a7cSnicm static int
117367c16a7cSnicm window_customize_set_command_callback(struct client *c, void *itemdata,
117467c16a7cSnicm     const char *s, __unused int done)
117567c16a7cSnicm {
117667c16a7cSnicm 	struct window_customize_itemdata	*item = itemdata;
117767c16a7cSnicm 	struct window_customize_modedata	*data = item->data;
117867c16a7cSnicm 	struct key_binding			*bd;
117967c16a7cSnicm 	struct cmd_parse_result			*pr;
118067c16a7cSnicm 	char					*error;
118167c16a7cSnicm 
118267c16a7cSnicm 	if (s == NULL || *s == '\0' || data->dead)
118367c16a7cSnicm 		return (0);
118467c16a7cSnicm 	if (item == NULL || !window_customize_get_key(item, NULL, &bd))
118567c16a7cSnicm 		return (0);
118667c16a7cSnicm 
118767c16a7cSnicm 	pr = cmd_parse_from_string(s, NULL);
118867c16a7cSnicm 	switch (pr->status) {
118967c16a7cSnicm 	case CMD_PARSE_ERROR:
119067c16a7cSnicm 		error = pr->error;
119167c16a7cSnicm 		goto fail;
119267c16a7cSnicm 	case CMD_PARSE_SUCCESS:
119367c16a7cSnicm 		break;
119467c16a7cSnicm 	}
119567c16a7cSnicm 	cmd_list_free(bd->cmdlist);
119667c16a7cSnicm 	bd->cmdlist = pr->cmdlist;
119767c16a7cSnicm 
119867c16a7cSnicm 	mode_tree_build(data->data);
119967c16a7cSnicm 	mode_tree_draw(data->data);
120067c16a7cSnicm 	data->wp->flags |= PANE_REDRAW;
120167c16a7cSnicm 
120267c16a7cSnicm 	return (0);
120367c16a7cSnicm 
120467c16a7cSnicm fail:
120567c16a7cSnicm 	*error = toupper((u_char)*error);
1206e7e79d0aSnicm 	status_message_set(c, -1, 1, 0, "%s", error);
120767c16a7cSnicm 	free(error);
120867c16a7cSnicm 	return (0);
120967c16a7cSnicm }
121067c16a7cSnicm 
121167c16a7cSnicm static int
121267c16a7cSnicm window_customize_set_note_callback(__unused struct client *c, void *itemdata,
121367c16a7cSnicm     const char *s, __unused int done)
121467c16a7cSnicm {
121567c16a7cSnicm 	struct window_customize_itemdata	*item = itemdata;
121667c16a7cSnicm 	struct window_customize_modedata	*data = item->data;
121767c16a7cSnicm 	struct key_binding			*bd;
121867c16a7cSnicm 
121967c16a7cSnicm 	if (s == NULL || *s == '\0' || data->dead)
122067c16a7cSnicm 		return (0);
122167c16a7cSnicm 	if (item == NULL || !window_customize_get_key(item, NULL, &bd))
122267c16a7cSnicm 		return (0);
122367c16a7cSnicm 
122467c16a7cSnicm 	free((void *)bd->note);
122567c16a7cSnicm 	bd->note = xstrdup(s);
122667c16a7cSnicm 
122767c16a7cSnicm 	mode_tree_build(data->data);
122867c16a7cSnicm 	mode_tree_draw(data->data);
122967c16a7cSnicm 	data->wp->flags |= PANE_REDRAW;
123067c16a7cSnicm 
123167c16a7cSnicm 	return (0);
123267c16a7cSnicm }
123367c16a7cSnicm 
123467c16a7cSnicm static void
123567c16a7cSnicm window_customize_set_key(struct client *c,
123667c16a7cSnicm     struct window_customize_modedata *data,
123767c16a7cSnicm     struct window_customize_itemdata *item)
123867c16a7cSnicm {
123967c16a7cSnicm 	key_code				 key = item->key;
124067c16a7cSnicm 	struct key_binding			*bd;
124167c16a7cSnicm 	const char				*s;
124267c16a7cSnicm 	char					*prompt, *value;
124367c16a7cSnicm 	struct window_customize_itemdata	*new_item;
124467c16a7cSnicm 
124567c16a7cSnicm 	if (item == NULL || !window_customize_get_key(item, NULL, &bd))
124667c16a7cSnicm 		return;
124767c16a7cSnicm 
124867c16a7cSnicm 	s = mode_tree_get_current_name(data->data);
124967c16a7cSnicm 	if (strcmp(s, "Repeat") == 0)
125067c16a7cSnicm 		bd->flags ^= KEY_BINDING_REPEAT;
125167c16a7cSnicm 	else if (strcmp(s, "Command") == 0) {
12525416581eSnicm 		xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0));
125367c16a7cSnicm 		value = cmd_list_print(bd->cmdlist, 0);
125467c16a7cSnicm 
125567c16a7cSnicm 		new_item = xcalloc(1, sizeof *new_item);
125667c16a7cSnicm 		new_item->data = data;
125767c16a7cSnicm 		new_item->scope = item->scope;
125867c16a7cSnicm 		new_item->table = xstrdup(item->table);
125967c16a7cSnicm 		new_item->key = key;
126067c16a7cSnicm 
126167c16a7cSnicm 		data->references++;
126294adf770Snicm 		status_prompt_set(c, NULL, prompt, value,
126367c16a7cSnicm 		    window_customize_set_command_callback,
126467c16a7cSnicm 		    window_customize_free_item_callback, new_item,
1265bc5a8fc2Snicm 		    PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
126667c16a7cSnicm 		free(prompt);
126767c16a7cSnicm 		free(value);
126867c16a7cSnicm 	} else if (strcmp(s, "Note") == 0) {
12695416581eSnicm 		xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0));
127067c16a7cSnicm 
127167c16a7cSnicm 		new_item = xcalloc(1, sizeof *new_item);
127267c16a7cSnicm 		new_item->data = data;
127367c16a7cSnicm 		new_item->scope = item->scope;
127467c16a7cSnicm 		new_item->table = xstrdup(item->table);
127567c16a7cSnicm 		new_item->key = key;
127667c16a7cSnicm 
127767c16a7cSnicm 		data->references++;
127894adf770Snicm 		status_prompt_set(c, NULL, prompt,
127994adf770Snicm 		    (bd->note == NULL ? "" : bd->note),
128067c16a7cSnicm 		    window_customize_set_note_callback,
128167c16a7cSnicm 		    window_customize_free_item_callback, new_item,
1282bc5a8fc2Snicm 		    PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
128367c16a7cSnicm 		free(prompt);
128467c16a7cSnicm 	}
128567c16a7cSnicm }
128667c16a7cSnicm 
128767c16a7cSnicm static void
128867c16a7cSnicm window_customize_unset_key(struct window_customize_modedata *data,
128967c16a7cSnicm     struct window_customize_itemdata *item)
129067c16a7cSnicm {
129167c16a7cSnicm 	struct key_table	*kt;
129267c16a7cSnicm 	struct key_binding	*bd;
129367c16a7cSnicm 
129467c16a7cSnicm 	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
129567c16a7cSnicm 		return;
129667c16a7cSnicm 
129767c16a7cSnicm 	if (item == mode_tree_get_current(data->data)) {
129867c16a7cSnicm 		mode_tree_collapse_current(data->data);
129967c16a7cSnicm 		mode_tree_up(data->data, 0);
130067c16a7cSnicm 	}
130167c16a7cSnicm 	key_bindings_remove(kt->name, bd->key);
130267c16a7cSnicm }
130367c16a7cSnicm 
130467c16a7cSnicm static void
130594c0d63cSnicm window_customize_reset_key(struct window_customize_modedata *data,
130694c0d63cSnicm     struct window_customize_itemdata *item)
130794c0d63cSnicm {
130894c0d63cSnicm 	struct key_table	*kt;
130994c0d63cSnicm 	struct key_binding	*dd, *bd;
131094c0d63cSnicm 
131194c0d63cSnicm 	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
131294c0d63cSnicm 		return;
131394c0d63cSnicm 
131494c0d63cSnicm 	dd = key_bindings_get_default(kt, bd->key);
131594c0d63cSnicm 	if (dd != NULL && bd->cmdlist == dd->cmdlist)
131694c0d63cSnicm 		return;
131794c0d63cSnicm 	if (dd == NULL && item == mode_tree_get_current(data->data)) {
131894c0d63cSnicm 		mode_tree_collapse_current(data->data);
131994c0d63cSnicm 		mode_tree_up(data->data, 0);
132094c0d63cSnicm 	}
132194c0d63cSnicm 	key_bindings_reset(kt->name, bd->key);
132294c0d63cSnicm }
132394c0d63cSnicm 
132494c0d63cSnicm static void
132594c0d63cSnicm window_customize_change_each(void *modedata, void *itemdata,
132667c16a7cSnicm     __unused struct client *c, __unused key_code key)
132767c16a7cSnicm {
132894c0d63cSnicm 	struct window_customize_modedata	*data = modedata;
132967c16a7cSnicm 	struct window_customize_itemdata	*item = itemdata;
133067c16a7cSnicm 
133194c0d63cSnicm 	switch (data->change) {
133294c0d63cSnicm 	case WINDOW_CUSTOMIZE_UNSET:
133367c16a7cSnicm 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
133494c0d63cSnicm 			window_customize_unset_key(data, item);
133594c0d63cSnicm 		else
133694c0d63cSnicm 			window_customize_unset_option(data, item);
133794c0d63cSnicm 		break;
133894c0d63cSnicm 	case WINDOW_CUSTOMIZE_RESET:
133994c0d63cSnicm 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
134094c0d63cSnicm 			window_customize_reset_key(data, item);
134194c0d63cSnicm 		else
134294c0d63cSnicm 			window_customize_reset_option(data, item);
134394c0d63cSnicm 		break;
134467c16a7cSnicm 	}
134594c0d63cSnicm 	if (item->scope != WINDOW_CUSTOMIZE_KEY)
134694c0d63cSnicm 		options_push_changes(item->name);
134767c16a7cSnicm }
134867c16a7cSnicm 
134967c16a7cSnicm static int
135094c0d63cSnicm window_customize_change_current_callback(__unused struct client *c,
135167c16a7cSnicm     void *modedata, const char *s, __unused int done)
135267c16a7cSnicm {
135367c16a7cSnicm 	struct window_customize_modedata	*data = modedata;
135467c16a7cSnicm 	struct window_customize_itemdata	*item;
135567c16a7cSnicm 
135667c16a7cSnicm 	if (s == NULL || *s == '\0' || data->dead)
135767c16a7cSnicm 		return (0);
135867c16a7cSnicm 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
135967c16a7cSnicm 		return (0);
136067c16a7cSnicm 
136167c16a7cSnicm 	item = mode_tree_get_current(data->data);
136294c0d63cSnicm 	switch (data->change) {
136394c0d63cSnicm 	case WINDOW_CUSTOMIZE_UNSET:
136467c16a7cSnicm 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
136567c16a7cSnicm 			window_customize_unset_key(data, item);
136694c0d63cSnicm 		else
136767c16a7cSnicm 			window_customize_unset_option(data, item);
136894c0d63cSnicm 		break;
136994c0d63cSnicm 	case WINDOW_CUSTOMIZE_RESET:
137094c0d63cSnicm 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
137194c0d63cSnicm 			window_customize_reset_key(data, item);
137294c0d63cSnicm 		else
137394c0d63cSnicm 			window_customize_reset_option(data, item);
137494c0d63cSnicm 		break;
137567c16a7cSnicm 	}
137694c0d63cSnicm 	if (item->scope != WINDOW_CUSTOMIZE_KEY)
137794c0d63cSnicm 		options_push_changes(item->name);
137867c16a7cSnicm 	mode_tree_build(data->data);
137967c16a7cSnicm 	mode_tree_draw(data->data);
138067c16a7cSnicm 	data->wp->flags |= PANE_REDRAW;
138167c16a7cSnicm 
138267c16a7cSnicm 	return (0);
138367c16a7cSnicm }
138467c16a7cSnicm 
138567c16a7cSnicm static int
138694c0d63cSnicm window_customize_change_tagged_callback(struct client *c, void *modedata,
138767c16a7cSnicm     const char *s, __unused int done)
138867c16a7cSnicm {
138967c16a7cSnicm 	struct window_customize_modedata	*data = modedata;
139067c16a7cSnicm 
139167c16a7cSnicm 	if (s == NULL || *s == '\0' || data->dead)
139267c16a7cSnicm 		return (0);
139367c16a7cSnicm 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
139467c16a7cSnicm 		return (0);
139567c16a7cSnicm 
139694c0d63cSnicm 	mode_tree_each_tagged(data->data, window_customize_change_each, c,
139767c16a7cSnicm 	    KEYC_NONE, 0);
139867c16a7cSnicm 	mode_tree_build(data->data);
139967c16a7cSnicm 	mode_tree_draw(data->data);
140067c16a7cSnicm 	data->wp->flags |= PANE_REDRAW;
140167c16a7cSnicm 
140267c16a7cSnicm 	return (0);
140367c16a7cSnicm }
140467c16a7cSnicm 
140567c16a7cSnicm static void
140667c16a7cSnicm window_customize_key(struct window_mode_entry *wme, struct client *c,
140767c16a7cSnicm     __unused struct session *s, __unused struct winlink *wl, key_code key,
140867c16a7cSnicm     struct mouse_event *m)
140967c16a7cSnicm {
141067c16a7cSnicm 	struct window_pane			*wp = wme->wp;
141167c16a7cSnicm 	struct window_customize_modedata	*data = wme->data;
141267c16a7cSnicm 	struct window_customize_itemdata	*item, *new_item;
141394c0d63cSnicm 	int					 finished, idx;
141467c16a7cSnicm 	char					*prompt;
141567c16a7cSnicm 	u_int					 tagged;
141667c16a7cSnicm 
141767c16a7cSnicm 	item = mode_tree_get_current(data->data);
141867c16a7cSnicm 	finished = mode_tree_key(data->data, c, &key, m, NULL, NULL);
141967c16a7cSnicm 	if (item != (new_item = mode_tree_get_current(data->data)))
142067c16a7cSnicm 		item = new_item;
142167c16a7cSnicm 
142267c16a7cSnicm 	switch (key) {
142367c16a7cSnicm 	case '\r':
142467c16a7cSnicm 	case 's':
142567c16a7cSnicm 		if (item == NULL)
142667c16a7cSnicm 			break;
142767c16a7cSnicm 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
142867c16a7cSnicm 			window_customize_set_key(c, data, item);
142967c16a7cSnicm 		else {
143067c16a7cSnicm 			window_customize_set_option(c, data, item, 0, 1);
143167c16a7cSnicm 			options_push_changes(item->name);
143267c16a7cSnicm 		}
143367c16a7cSnicm 		mode_tree_build(data->data);
143467c16a7cSnicm 		break;
143567c16a7cSnicm 	case 'w':
143667c16a7cSnicm 		if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY)
143767c16a7cSnicm 			break;
143867c16a7cSnicm 		window_customize_set_option(c, data, item, 0, 0);
143967c16a7cSnicm 		options_push_changes(item->name);
144067c16a7cSnicm 		mode_tree_build(data->data);
144167c16a7cSnicm 		break;
144267c16a7cSnicm 	case 'S':
144367c16a7cSnicm 	case 'W':
144467c16a7cSnicm 		if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY)
144567c16a7cSnicm 			break;
144667c16a7cSnicm 		window_customize_set_option(c, data, item, 1, 0);
144767c16a7cSnicm 		options_push_changes(item->name);
144867c16a7cSnicm 		mode_tree_build(data->data);
144967c16a7cSnicm 		break;
145094c0d63cSnicm 	case 'd':
145194c0d63cSnicm 		if (item == NULL || item->idx != -1)
145294c0d63cSnicm 			break;
145394c0d63cSnicm 		xasprintf(&prompt, "Reset %s to default? ", item->name);
145494c0d63cSnicm 		data->references++;
145594c0d63cSnicm 		data->change = WINDOW_CUSTOMIZE_RESET;
145694c0d63cSnicm 		status_prompt_set(c, NULL, prompt, "",
145794c0d63cSnicm 		    window_customize_change_current_callback,
145894c0d63cSnicm 		    window_customize_free_callback, data,
1459*af11b61dSnicm 		    PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
1460*af11b61dSnicm 		    PROMPT_TYPE_COMMAND);
146194c0d63cSnicm 		free(prompt);
146294c0d63cSnicm 		break;
146394c0d63cSnicm 	case 'D':
146494c0d63cSnicm 		tagged = mode_tree_count_tagged(data->data);
146594c0d63cSnicm 		if (tagged == 0)
146694c0d63cSnicm 			break;
146794c0d63cSnicm 		xasprintf(&prompt, "Reset %u tagged to default? ", tagged);
146894c0d63cSnicm 		data->references++;
146994c0d63cSnicm 		data->change = WINDOW_CUSTOMIZE_RESET;
147094c0d63cSnicm 		status_prompt_set(c, NULL, prompt, "",
147194c0d63cSnicm 		    window_customize_change_tagged_callback,
147294c0d63cSnicm 		    window_customize_free_callback, data,
1473*af11b61dSnicm 		    PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
1474*af11b61dSnicm 		    PROMPT_TYPE_COMMAND);
147594c0d63cSnicm 		free(prompt);
147694c0d63cSnicm 		break;
147767c16a7cSnicm 	case 'u':
147867c16a7cSnicm 		if (item == NULL)
147967c16a7cSnicm 			break;
148094c0d63cSnicm 		idx = item->idx;
148194c0d63cSnicm 		if (idx != -1)
148294c0d63cSnicm 			xasprintf(&prompt, "Unset %s[%d]? ", item->name, idx);
148394c0d63cSnicm 		else
148494c0d63cSnicm 			xasprintf(&prompt, "Unset %s? ", item->name);
148567c16a7cSnicm 		data->references++;
148694c0d63cSnicm 		data->change = WINDOW_CUSTOMIZE_UNSET;
148794adf770Snicm 		status_prompt_set(c, NULL, prompt, "",
148894c0d63cSnicm 		    window_customize_change_current_callback,
148967c16a7cSnicm 		    window_customize_free_callback, data,
1490*af11b61dSnicm 		    PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
1491*af11b61dSnicm 		    PROMPT_TYPE_COMMAND);
149267c16a7cSnicm 		free(prompt);
149367c16a7cSnicm 		break;
149467c16a7cSnicm 	case 'U':
149567c16a7cSnicm 		tagged = mode_tree_count_tagged(data->data);
149667c16a7cSnicm 		if (tagged == 0)
149767c16a7cSnicm 			break;
149894c0d63cSnicm 		xasprintf(&prompt, "Unset %u tagged? ", tagged);
149967c16a7cSnicm 		data->references++;
150094c0d63cSnicm 		data->change = WINDOW_CUSTOMIZE_UNSET;
150194adf770Snicm 		status_prompt_set(c, NULL, prompt, "",
150294c0d63cSnicm 		    window_customize_change_tagged_callback,
150367c16a7cSnicm 		    window_customize_free_callback, data,
1504*af11b61dSnicm 		    PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
1505*af11b61dSnicm 		    PROMPT_TYPE_COMMAND);
150667c16a7cSnicm 		free(prompt);
150767c16a7cSnicm 		break;
150867c16a7cSnicm 	case 'H':
150967c16a7cSnicm 		data->hide_global = !data->hide_global;
151067c16a7cSnicm 		mode_tree_build(data->data);
151167c16a7cSnicm 		break;
151267c16a7cSnicm 	}
151367c16a7cSnicm 	if (finished)
151467c16a7cSnicm 		window_pane_reset_mode(wp);
151567c16a7cSnicm 	else {
151667c16a7cSnicm 		mode_tree_draw(data->data);
151767c16a7cSnicm 		wp->flags |= PANE_REDRAW;
151867c16a7cSnicm 	}
151967c16a7cSnicm }
1520