xref: /netbsd-src/external/bsd/tmux/dist/window-customize.c (revision 46548964d21002ed96d2f9f7eeb6357c81c3425c)
19fb66d81Schristos /* $OpenBSD$ */
29fb66d81Schristos 
39fb66d81Schristos /*
49fb66d81Schristos  * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
59fb66d81Schristos  *
69fb66d81Schristos  * Permission to use, copy, modify, and distribute this software for any
79fb66d81Schristos  * purpose with or without fee is hereby granted, provided that the above
89fb66d81Schristos  * copyright notice and this permission notice appear in all copies.
99fb66d81Schristos  *
109fb66d81Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
119fb66d81Schristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
129fb66d81Schristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
139fb66d81Schristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
149fb66d81Schristos  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
159fb66d81Schristos  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
169fb66d81Schristos  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
179fb66d81Schristos  */
189fb66d81Schristos 
199fb66d81Schristos #include <sys/types.h>
209fb66d81Schristos 
219fb66d81Schristos #include <ctype.h>
229fb66d81Schristos #include <stdlib.h>
239fb66d81Schristos #include <string.h>
249fb66d81Schristos 
259fb66d81Schristos #include "tmux.h"
269fb66d81Schristos 
279fb66d81Schristos static struct screen	*window_customize_init(struct window_mode_entry *,
289fb66d81Schristos 			     struct cmd_find_state *, struct args *);
299fb66d81Schristos static void		 window_customize_free(struct window_mode_entry *);
309fb66d81Schristos static void		 window_customize_resize(struct window_mode_entry *,
319fb66d81Schristos 			      u_int, u_int);
329fb66d81Schristos static void		 window_customize_key(struct window_mode_entry *,
339fb66d81Schristos 			     struct client *, struct session *,
349fb66d81Schristos 			     struct winlink *, key_code, struct mouse_event *);
359fb66d81Schristos 
369fb66d81Schristos #define WINDOW_CUSTOMIZE_DEFAULT_FORMAT \
379fb66d81Schristos 	"#{?is_option," \
389fb66d81Schristos 		"#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \
399fb66d81Schristos 		"#[ignore]" \
409fb66d81Schristos 		"#{option_value}#{?option_unit, #{option_unit},}" \
419fb66d81Schristos 	"," \
429fb66d81Schristos 		"#{key}" \
439fb66d81Schristos 	"}"
449fb66d81Schristos 
459fb66d81Schristos static const struct menu_item window_customize_menu_items[] = {
469fb66d81Schristos 	{ "Select", '\r', NULL },
479fb66d81Schristos 	{ "Expand", KEYC_RIGHT, NULL },
489fb66d81Schristos 	{ "", KEYC_NONE, NULL },
499fb66d81Schristos 	{ "Tag", 't', NULL },
509fb66d81Schristos 	{ "Tag All", '\024', NULL },
519fb66d81Schristos 	{ "Tag None", 'T', NULL },
529fb66d81Schristos 	{ "", KEYC_NONE, NULL },
539fb66d81Schristos 	{ "Cancel", 'q', NULL },
549fb66d81Schristos 
559fb66d81Schristos 	{ NULL, KEYC_NONE, NULL }
569fb66d81Schristos };
579fb66d81Schristos 
589fb66d81Schristos const struct window_mode window_customize_mode = {
599fb66d81Schristos 	.name = "options-mode",
609fb66d81Schristos 	.default_format = WINDOW_CUSTOMIZE_DEFAULT_FORMAT,
619fb66d81Schristos 
629fb66d81Schristos 	.init = window_customize_init,
639fb66d81Schristos 	.free = window_customize_free,
649fb66d81Schristos 	.resize = window_customize_resize,
659fb66d81Schristos 	.key = window_customize_key,
669fb66d81Schristos };
679fb66d81Schristos 
689fb66d81Schristos enum window_customize_scope {
699fb66d81Schristos 	WINDOW_CUSTOMIZE_NONE,
709fb66d81Schristos 	WINDOW_CUSTOMIZE_KEY,
719fb66d81Schristos 	WINDOW_CUSTOMIZE_SERVER,
729fb66d81Schristos 	WINDOW_CUSTOMIZE_GLOBAL_SESSION,
739fb66d81Schristos 	WINDOW_CUSTOMIZE_SESSION,
749fb66d81Schristos 	WINDOW_CUSTOMIZE_GLOBAL_WINDOW,
759fb66d81Schristos 	WINDOW_CUSTOMIZE_WINDOW,
769fb66d81Schristos 	WINDOW_CUSTOMIZE_PANE
779fb66d81Schristos };
789fb66d81Schristos 
799fb66d81Schristos enum window_customize_change {
809fb66d81Schristos 	WINDOW_CUSTOMIZE_UNSET,
819fb66d81Schristos 	WINDOW_CUSTOMIZE_RESET,
829fb66d81Schristos };
839fb66d81Schristos 
849fb66d81Schristos struct window_customize_itemdata {
859fb66d81Schristos 	struct window_customize_modedata	*data;
869fb66d81Schristos 	enum window_customize_scope		 scope;
879fb66d81Schristos 
889fb66d81Schristos 	char					*table;
899fb66d81Schristos 	key_code				 key;
909fb66d81Schristos 
919fb66d81Schristos 	struct options				*oo;
929fb66d81Schristos 	char					*name;
939fb66d81Schristos 	int					 idx;
949fb66d81Schristos };
959fb66d81Schristos 
969fb66d81Schristos struct window_customize_modedata {
979fb66d81Schristos 	struct window_pane			 *wp;
989fb66d81Schristos 	int					  dead;
999fb66d81Schristos 	int					  references;
1009fb66d81Schristos 
1019fb66d81Schristos 	struct mode_tree_data			 *data;
1029fb66d81Schristos 	char					 *format;
1039fb66d81Schristos 	int					  hide_global;
1049fb66d81Schristos 
1059fb66d81Schristos 	struct window_customize_itemdata	**item_list;
1069fb66d81Schristos 	u_int					  item_size;
1079fb66d81Schristos 
1089fb66d81Schristos 	struct cmd_find_state			  fs;
1099fb66d81Schristos 	enum window_customize_change		  change;
1109fb66d81Schristos };
1119fb66d81Schristos 
1129fb66d81Schristos static uint64_t
window_customize_get_tag(struct options_entry * o,int idx,const struct options_table_entry * oe)1139fb66d81Schristos window_customize_get_tag(struct options_entry *o, int idx,
1149fb66d81Schristos     const struct options_table_entry *oe)
1159fb66d81Schristos {
1169fb66d81Schristos 	uint64_t	offset;
1179fb66d81Schristos 
1189fb66d81Schristos 	if (oe == NULL)
119c745c111Schristos 		return ((uintptr_t)o);
120e271dbb8Schristos 	offset = ((const char *)oe - (const char *)options_table) / sizeof *options_table;
1219fb66d81Schristos 	return ((2ULL << 62)|(offset << 32)|((idx + 1) << 1)|1);
1229fb66d81Schristos }
1239fb66d81Schristos 
1249fb66d81Schristos static struct options *
window_customize_get_tree(enum window_customize_scope scope,struct cmd_find_state * fs)1259fb66d81Schristos window_customize_get_tree(enum window_customize_scope scope,
1269fb66d81Schristos     struct cmd_find_state *fs)
1279fb66d81Schristos {
1289fb66d81Schristos 	switch (scope) {
1299fb66d81Schristos 	case WINDOW_CUSTOMIZE_NONE:
1309fb66d81Schristos 	case WINDOW_CUSTOMIZE_KEY:
1319fb66d81Schristos 		return (NULL);
1329fb66d81Schristos 	case WINDOW_CUSTOMIZE_SERVER:
1339fb66d81Schristos 		return (global_options);
1349fb66d81Schristos 	case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
1359fb66d81Schristos 		return (global_s_options);
1369fb66d81Schristos 	case WINDOW_CUSTOMIZE_SESSION:
1379fb66d81Schristos 		return (fs->s->options);
1389fb66d81Schristos 	case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
1399fb66d81Schristos 		return (global_w_options);
1409fb66d81Schristos 	case WINDOW_CUSTOMIZE_WINDOW:
1419fb66d81Schristos 		return (fs->w->options);
1429fb66d81Schristos 	case WINDOW_CUSTOMIZE_PANE:
1439fb66d81Schristos 		return (fs->wp->options);
1449fb66d81Schristos 	}
1459fb66d81Schristos 	return (NULL);
1469fb66d81Schristos }
1479fb66d81Schristos 
1489fb66d81Schristos static int
window_customize_check_item(struct window_customize_modedata * data,struct window_customize_itemdata * item,struct cmd_find_state * fsp)1499fb66d81Schristos window_customize_check_item(struct window_customize_modedata *data,
1509fb66d81Schristos     struct window_customize_itemdata *item, struct cmd_find_state *fsp)
1519fb66d81Schristos {
1529fb66d81Schristos 	struct cmd_find_state	fs;
1539fb66d81Schristos 
1549fb66d81Schristos 	if (fsp == NULL)
1559fb66d81Schristos 		fsp = &fs;
1569fb66d81Schristos 
1579fb66d81Schristos 	if (cmd_find_valid_state(&data->fs))
1589fb66d81Schristos 		cmd_find_copy_state(fsp, &data->fs);
1599fb66d81Schristos 	else
1609fb66d81Schristos 		cmd_find_from_pane(fsp, data->wp, 0);
1619fb66d81Schristos 	return (item->oo == window_customize_get_tree(item->scope, fsp));
1629fb66d81Schristos }
1639fb66d81Schristos 
1649fb66d81Schristos static int
window_customize_get_key(struct window_customize_itemdata * item,struct key_table ** ktp,struct key_binding ** bdp)1659fb66d81Schristos window_customize_get_key(struct window_customize_itemdata *item,
1669fb66d81Schristos     struct key_table **ktp, struct key_binding **bdp)
1679fb66d81Schristos {
1689fb66d81Schristos 	struct key_table	*kt;
1699fb66d81Schristos 	struct key_binding	*bd;
1709fb66d81Schristos 
1719fb66d81Schristos 	kt = key_bindings_get_table(item->table, 0);
1729fb66d81Schristos 	if (kt == NULL)
1739fb66d81Schristos 		return (0);
1749fb66d81Schristos 	bd = key_bindings_get(kt, item->key);
1759fb66d81Schristos 	if (bd == NULL)
1769fb66d81Schristos 		return (0);
1779fb66d81Schristos 
1789fb66d81Schristos 	if (ktp != NULL)
1799fb66d81Schristos 		*ktp = kt;
1809fb66d81Schristos 	if (bdp != NULL)
1819fb66d81Schristos 		*bdp = bd;
1829fb66d81Schristos 	return (1);
1839fb66d81Schristos }
1849fb66d81Schristos 
1859fb66d81Schristos static char *
window_customize_scope_text(enum window_customize_scope scope,struct cmd_find_state * fs)1869fb66d81Schristos window_customize_scope_text(enum window_customize_scope scope,
1879fb66d81Schristos     struct cmd_find_state *fs)
1889fb66d81Schristos {
1899fb66d81Schristos 	char	*s;
1909fb66d81Schristos 	u_int	 idx;
1919fb66d81Schristos 
1929fb66d81Schristos 	switch (scope) {
1939fb66d81Schristos 	case WINDOW_CUSTOMIZE_PANE:
1949fb66d81Schristos 		window_pane_index(fs->wp, &idx);
1959fb66d81Schristos 		xasprintf(&s, "pane %u", idx);
1969fb66d81Schristos 		break;
1979fb66d81Schristos 	case WINDOW_CUSTOMIZE_SESSION:
1989fb66d81Schristos 		xasprintf(&s, "session %s", fs->s->name);
1999fb66d81Schristos 		break;
2009fb66d81Schristos 	case WINDOW_CUSTOMIZE_WINDOW:
2019fb66d81Schristos 		xasprintf(&s, "window %u", fs->wl->idx);
2029fb66d81Schristos 		break;
2039fb66d81Schristos 	default:
2049fb66d81Schristos 		s = xstrdup("");
2059fb66d81Schristos 		break;
2069fb66d81Schristos 	}
2079fb66d81Schristos 	return (s);
2089fb66d81Schristos }
2099fb66d81Schristos 
2109fb66d81Schristos static struct window_customize_itemdata *
window_customize_add_item(struct window_customize_modedata * data)2119fb66d81Schristos window_customize_add_item(struct window_customize_modedata *data)
2129fb66d81Schristos {
2139fb66d81Schristos 	struct window_customize_itemdata	*item;
2149fb66d81Schristos 
2159fb66d81Schristos 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
2169fb66d81Schristos 	    sizeof *data->item_list);
2179fb66d81Schristos 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
2189fb66d81Schristos 	return (item);
2199fb66d81Schristos }
2209fb66d81Schristos 
2219fb66d81Schristos static void
window_customize_free_item(struct window_customize_itemdata * item)2229fb66d81Schristos window_customize_free_item(struct window_customize_itemdata *item)
2239fb66d81Schristos {
2249fb66d81Schristos 	free(item->table);
2259fb66d81Schristos 	free(item->name);
2269fb66d81Schristos 	free(item);
2279fb66d81Schristos }
2289fb66d81Schristos 
2299fb66d81Schristos static void
window_customize_build_array(struct window_customize_modedata * data,struct mode_tree_item * top,enum window_customize_scope scope,struct options_entry * o,struct format_tree * ft)2309fb66d81Schristos window_customize_build_array(struct window_customize_modedata *data,
2319fb66d81Schristos     struct mode_tree_item *top, enum window_customize_scope scope,
2329fb66d81Schristos     struct options_entry *o, struct format_tree *ft)
2339fb66d81Schristos {
2349fb66d81Schristos 	const struct options_table_entry	*oe = options_table_entry(o);
2359fb66d81Schristos 	struct options				*oo = options_owner(o);
2369fb66d81Schristos 	struct window_customize_itemdata	*item;
2379fb66d81Schristos 	struct options_array_item		*ai;
2389fb66d81Schristos 	char					*name, *value, *text;
2399fb66d81Schristos 	u_int					 idx;
2409fb66d81Schristos 	uint64_t				 tag;
2419fb66d81Schristos 
2429fb66d81Schristos 	ai = options_array_first(o);
2439fb66d81Schristos 	while (ai != NULL) {
2449fb66d81Schristos 		idx = options_array_item_index(ai);
2459fb66d81Schristos 
2469fb66d81Schristos 		xasprintf(&name, "%s[%u]", options_name(o), idx);
2479fb66d81Schristos 		format_add(ft, "option_name", "%s", name);
2489fb66d81Schristos 		value = options_to_string(o, idx, 0);
2499fb66d81Schristos 		format_add(ft, "option_value", "%s", value);
2509fb66d81Schristos 
2519fb66d81Schristos 		item = window_customize_add_item(data);
2529fb66d81Schristos 		item->scope = scope;
2539fb66d81Schristos 		item->oo = oo;
2549fb66d81Schristos 		item->name = xstrdup(options_name(o));
2559fb66d81Schristos 		item->idx = idx;
2569fb66d81Schristos 
2579fb66d81Schristos 		text = format_expand(ft, data->format);
2589fb66d81Schristos 		tag = window_customize_get_tag(o, idx, oe);
2599fb66d81Schristos 		mode_tree_add(data->data, top, item, tag, name, text, -1);
2609fb66d81Schristos 		free(text);
2619fb66d81Schristos 
2629fb66d81Schristos 		free(name);
2639fb66d81Schristos 		free(value);
2649fb66d81Schristos 
2659fb66d81Schristos 		ai = options_array_next(ai);
2669fb66d81Schristos 	}
2679fb66d81Schristos }
2689fb66d81Schristos 
2699fb66d81Schristos static void
window_customize_build_option(struct window_customize_modedata * data,struct mode_tree_item * top,enum window_customize_scope scope,struct options_entry * o,struct format_tree * ft,const char * filter,struct cmd_find_state * fs)2709fb66d81Schristos window_customize_build_option(struct window_customize_modedata *data,
2719fb66d81Schristos     struct mode_tree_item *top, enum window_customize_scope scope,
2729fb66d81Schristos     struct options_entry *o, struct format_tree *ft,
2739fb66d81Schristos     const char *filter, struct cmd_find_state *fs)
2749fb66d81Schristos {
2759fb66d81Schristos 	const struct options_table_entry	*oe = options_table_entry(o);
2769fb66d81Schristos 	struct options				*oo = options_owner(o);
2779fb66d81Schristos 	const char				*name = options_name(o);
2789fb66d81Schristos 	struct window_customize_itemdata	*item;
2799fb66d81Schristos 	char					*text, *expanded, *value;
2809fb66d81Schristos 	int					 global = 0, array = 0;
2819fb66d81Schristos 	uint64_t				 tag;
2829fb66d81Schristos 
2839fb66d81Schristos 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
2849fb66d81Schristos 		return;
2859fb66d81Schristos 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY))
2869fb66d81Schristos 		array = 1;
2879fb66d81Schristos 
2889fb66d81Schristos 	if (scope == WINDOW_CUSTOMIZE_SERVER ||
2899fb66d81Schristos 	    scope == WINDOW_CUSTOMIZE_GLOBAL_SESSION ||
2909fb66d81Schristos 	    scope == WINDOW_CUSTOMIZE_GLOBAL_WINDOW)
2919fb66d81Schristos 		global = 1;
2929fb66d81Schristos 	if (data->hide_global && global)
2939fb66d81Schristos 		return;
2949fb66d81Schristos 
2959fb66d81Schristos 	format_add(ft, "option_name", "%s", name);
2969fb66d81Schristos 	format_add(ft, "option_is_global", "%d", global);
2979fb66d81Schristos 	format_add(ft, "option_is_array", "%d", array);
2989fb66d81Schristos 
2999fb66d81Schristos 	text = window_customize_scope_text(scope, fs);
3009fb66d81Schristos 	format_add(ft, "option_scope", "%s", text);
3019fb66d81Schristos 	free(text);
3029fb66d81Schristos 
3039fb66d81Schristos 	if (oe != NULL && oe->unit != NULL)
3049fb66d81Schristos 		format_add(ft, "option_unit", "%s", oe->unit);
3059fb66d81Schristos 	else
3069fb66d81Schristos 		format_add(ft, "option_unit", "%s", "");
3079fb66d81Schristos 
3089fb66d81Schristos 	if (!array) {
3099fb66d81Schristos 		value = options_to_string(o, -1, 0);
3109fb66d81Schristos 		format_add(ft, "option_value", "%s", value);
3119fb66d81Schristos 		free(value);
3129fb66d81Schristos 	}
3139fb66d81Schristos 
3149fb66d81Schristos 	if (filter != NULL) {
3159fb66d81Schristos 		expanded = format_expand(ft, filter);
3169fb66d81Schristos 		if (!format_true(expanded)) {
3179fb66d81Schristos 			free(expanded);
3189fb66d81Schristos 			return;
3199fb66d81Schristos 		}
3209fb66d81Schristos 		free(expanded);
3219fb66d81Schristos 	}
3229fb66d81Schristos 	item = window_customize_add_item(data);
3239fb66d81Schristos 	item->oo = oo;
3249fb66d81Schristos 	item->scope = scope;
3259fb66d81Schristos 	item->name = xstrdup(name);
3269fb66d81Schristos 	item->idx = -1;
3279fb66d81Schristos 
3289fb66d81Schristos 	if (array)
3299fb66d81Schristos 		text = NULL;
3309fb66d81Schristos 	else
3319fb66d81Schristos 		text = format_expand(ft, data->format);
3329fb66d81Schristos 	tag = window_customize_get_tag(o, -1, oe);
3339fb66d81Schristos 	top = mode_tree_add(data->data, top, item, tag, name, text, 0);
3349fb66d81Schristos 	free(text);
3359fb66d81Schristos 
3369fb66d81Schristos 	if (array)
3379fb66d81Schristos 		window_customize_build_array(data, top, scope, o, ft);
3389fb66d81Schristos }
3399fb66d81Schristos 
3409fb66d81Schristos static void
window_customize_find_user_options(struct options * oo,const char *** list,u_int * size)3419fb66d81Schristos window_customize_find_user_options(struct options *oo, const char ***list,
3429fb66d81Schristos     u_int *size)
3439fb66d81Schristos {
3449fb66d81Schristos 	struct options_entry	*o;
3459fb66d81Schristos 	const char		*name;
3469fb66d81Schristos 	u_int			 i;
3479fb66d81Schristos 
3489fb66d81Schristos 	o = options_first(oo);
3499fb66d81Schristos 	while (o != NULL) {
3509fb66d81Schristos 		name = options_name(o);
3519fb66d81Schristos 		if (*name != '@') {
3529fb66d81Schristos 			o = options_next(o);
3539fb66d81Schristos 			continue;
3549fb66d81Schristos 		}
3559fb66d81Schristos 		for (i = 0; i < *size; i++) {
3569fb66d81Schristos 			if (strcmp((*list)[i], name) == 0)
3579fb66d81Schristos 				break;
3589fb66d81Schristos 		}
3599fb66d81Schristos 		if (i != *size) {
3609fb66d81Schristos 			o = options_next(o);
3619fb66d81Schristos 			continue;
3629fb66d81Schristos 		}
3639fb66d81Schristos 		*list = xreallocarray(*list, (*size) + 1, sizeof **list);
3649fb66d81Schristos 		(*list)[(*size)++] = name;
3659fb66d81Schristos 
3669fb66d81Schristos 		o = options_next(o);
3679fb66d81Schristos 	}
3689fb66d81Schristos }
3699fb66d81Schristos 
3709fb66d81Schristos static void
window_customize_build_options(struct window_customize_modedata * data,const char * title,uint64_t tag,enum window_customize_scope scope0,struct options * oo0,enum window_customize_scope scope1,struct options * oo1,enum window_customize_scope scope2,struct options * oo2,struct format_tree * ft,const char * filter,struct cmd_find_state * fs)3719fb66d81Schristos window_customize_build_options(struct window_customize_modedata *data,
3729fb66d81Schristos     const char *title, uint64_t tag,
3739fb66d81Schristos     enum window_customize_scope scope0, struct options *oo0,
3749fb66d81Schristos     enum window_customize_scope scope1, struct options *oo1,
3759fb66d81Schristos     enum window_customize_scope scope2, struct options *oo2,
3769fb66d81Schristos     struct format_tree *ft, const char *filter, struct cmd_find_state *fs)
3779fb66d81Schristos {
3789fb66d81Schristos 	struct mode_tree_item		 *top;
3799fb66d81Schristos 	struct options_entry		 *o = NULL, *loop;
3809fb66d81Schristos 	const char			**list = NULL, *name;
3819fb66d81Schristos 	u_int				  size = 0, i;
3829fb66d81Schristos 	enum window_customize_scope	  scope;
3839fb66d81Schristos 
3849fb66d81Schristos 	top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
3859fb66d81Schristos 	mode_tree_no_tag(top);
3869fb66d81Schristos 
3879fb66d81Schristos 	/*
3889fb66d81Schristos 	 * We get the options from the first tree, but build it using the
3899fb66d81Schristos 	 * values from the other two. Any tree can have user options so we need
3909fb66d81Schristos 	 * to build a separate list of them.
3919fb66d81Schristos 	 */
3929fb66d81Schristos 
3939fb66d81Schristos 	window_customize_find_user_options(oo0, &list, &size);
3949fb66d81Schristos 	if (oo1 != NULL)
3959fb66d81Schristos 		window_customize_find_user_options(oo1, &list, &size);
3969fb66d81Schristos 	if (oo2 != NULL)
3979fb66d81Schristos 		window_customize_find_user_options(oo2, &list, &size);
3989fb66d81Schristos 
3999fb66d81Schristos 	for (i = 0; i < size; i++) {
4009fb66d81Schristos 		if (oo2 != NULL)
401*46548964Swiz 			o = options_get(oo2, list[i]);
4029fb66d81Schristos 		if (o == NULL && oo1 != NULL)
4039fb66d81Schristos 			o = options_get(oo1, list[i]);
4049fb66d81Schristos 		if (o == NULL)
405*46548964Swiz 			o = options_get(oo0, list[i]);
4069fb66d81Schristos 		if (options_owner(o) == oo2)
4079fb66d81Schristos 			scope = scope2;
4089fb66d81Schristos 		else if (options_owner(o) == oo1)
4099fb66d81Schristos 			scope = scope1;
4109fb66d81Schristos 		else
4119fb66d81Schristos 			scope = scope0;
4129fb66d81Schristos 		window_customize_build_option(data, top, scope, o, ft, filter,
4139fb66d81Schristos 		    fs);
4149fb66d81Schristos 	}
4159fb66d81Schristos 	free(list);
4169fb66d81Schristos 
4179fb66d81Schristos 	loop = options_first(oo0);
4189fb66d81Schristos 	while (loop != NULL) {
4199fb66d81Schristos 		name = options_name(loop);
4209fb66d81Schristos 		if (*name == '@') {
4219fb66d81Schristos 			loop = options_next(loop);
4229fb66d81Schristos 			continue;
4239fb66d81Schristos 		}
4249fb66d81Schristos 		if (oo2 != NULL)
4259fb66d81Schristos 			o = options_get(oo2, name);
4269fb66d81Schristos 		else if (oo1 != NULL)
4279fb66d81Schristos 			o = options_get(oo1, name);
4289fb66d81Schristos 		else
4299fb66d81Schristos 			o = loop;
4309fb66d81Schristos 		if (options_owner(o) == oo2)
4319fb66d81Schristos 			scope = scope2;
4329fb66d81Schristos 		else if (options_owner(o) == oo1)
4339fb66d81Schristos 			scope = scope1;
4349fb66d81Schristos 		else
4359fb66d81Schristos 			scope = scope0;
4369fb66d81Schristos 		window_customize_build_option(data, top, scope, o, ft, filter,
4379fb66d81Schristos 		    fs);
4389fb66d81Schristos 		loop = options_next(loop);
4399fb66d81Schristos 	}
4409fb66d81Schristos }
4419fb66d81Schristos 
4429fb66d81Schristos static void
window_customize_build_keys(struct window_customize_modedata * data,struct key_table * kt,struct format_tree * ft,const char * filter,struct cmd_find_state * fs,u_int number)4439fb66d81Schristos window_customize_build_keys(struct window_customize_modedata *data,
4449fb66d81Schristos     struct key_table *kt, struct format_tree *ft, const char *filter,
4459fb66d81Schristos     struct cmd_find_state *fs, u_int number)
4469fb66d81Schristos {
4479fb66d81Schristos 	struct mode_tree_item			*top, *child, *mti;
4489fb66d81Schristos 	struct window_customize_itemdata	*item;
4499fb66d81Schristos 	struct key_binding			*bd;
4509fb66d81Schristos 	char					*title, *text, *tmp, *expanded;
4519fb66d81Schristos 	const char				*flag;
4529fb66d81Schristos 	uint64_t				 tag;
4539fb66d81Schristos 
4549fb66d81Schristos 	tag = (1ULL << 62)|((uint64_t)number << 54)|1;
4559fb66d81Schristos 
4569fb66d81Schristos 	xasprintf(&title, "Key Table - %s", kt->name);
4579fb66d81Schristos 	top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
4589fb66d81Schristos 	mode_tree_no_tag(top);
4599fb66d81Schristos 	free(title);
4609fb66d81Schristos 
4619fb66d81Schristos 	ft = format_create_from_state(NULL, NULL, fs);
4629fb66d81Schristos 	format_add(ft, "is_option", "0");
4639fb66d81Schristos 	format_add(ft, "is_key", "1");
4649fb66d81Schristos 
4659fb66d81Schristos 	bd = key_bindings_first(kt);
4669fb66d81Schristos 	while (bd != NULL) {
4679fb66d81Schristos 		format_add(ft, "key", "%s", key_string_lookup_key(bd->key, 0));
4689fb66d81Schristos 		if (bd->note != NULL)
4699fb66d81Schristos 			format_add(ft, "key_note", "%s", bd->note);
4709fb66d81Schristos 		if (filter != NULL) {
4719fb66d81Schristos 			expanded = format_expand(ft, filter);
4729fb66d81Schristos 			if (!format_true(expanded)) {
4739fb66d81Schristos 				free(expanded);
4749fb66d81Schristos 				continue;
4759fb66d81Schristos 			}
4769fb66d81Schristos 			free(expanded);
4779fb66d81Schristos 		}
4789fb66d81Schristos 
4799fb66d81Schristos 		item = window_customize_add_item(data);
4809fb66d81Schristos 		item->scope = WINDOW_CUSTOMIZE_KEY;
4819fb66d81Schristos 		item->table = xstrdup(kt->name);
4829fb66d81Schristos 		item->key = bd->key;
4839fb66d81Schristos 		item->name = xstrdup(key_string_lookup_key(item->key, 0));
4849fb66d81Schristos 		item->idx = -1;
4859fb66d81Schristos 
4869fb66d81Schristos 		expanded = format_expand(ft, data->format);
487c745c111Schristos 		child = mode_tree_add(data->data, top, item, (uintptr_t)bd,
4889fb66d81Schristos 		    expanded, NULL, 0);
4899fb66d81Schristos 		free(expanded);
4909fb66d81Schristos 
4919fb66d81Schristos 		tmp = cmd_list_print(bd->cmdlist, 0);
4929fb66d81Schristos 		xasprintf(&text, "#[ignore]%s", tmp);
4939fb66d81Schristos 		free(tmp);
4949fb66d81Schristos 		mti = mode_tree_add(data->data, child, item,
4959fb66d81Schristos 		    tag|(bd->key << 3)|(0 << 1)|1, "Command", text, -1);
4969fb66d81Schristos 		mode_tree_draw_as_parent(mti);
4979fb66d81Schristos 		mode_tree_no_tag(mti);
4989fb66d81Schristos 		free(text);
4999fb66d81Schristos 
5009fb66d81Schristos 		if (bd->note != NULL)
5019fb66d81Schristos 			xasprintf(&text, "#[ignore]%s", bd->note);
5029fb66d81Schristos 		else
5039fb66d81Schristos 			text = xstrdup("");
5049fb66d81Schristos 		mti = mode_tree_add(data->data, child, item,
5059fb66d81Schristos 		    tag|(bd->key << 3)|(1 << 1)|1, "Note", text, -1);
5069fb66d81Schristos 		mode_tree_draw_as_parent(mti);
5079fb66d81Schristos 		mode_tree_no_tag(mti);
5089fb66d81Schristos 		free(text);
5099fb66d81Schristos 
5109fb66d81Schristos 		if (bd->flags & KEY_BINDING_REPEAT)
5119fb66d81Schristos 			flag = "on";
5129fb66d81Schristos 		else
5139fb66d81Schristos 			flag = "off";
5149fb66d81Schristos 		mti = mode_tree_add(data->data, child, item,
5159fb66d81Schristos 		    tag|(bd->key << 3)|(2 << 1)|1, "Repeat", flag, -1);
5169fb66d81Schristos 		mode_tree_draw_as_parent(mti);
5179fb66d81Schristos 		mode_tree_no_tag(mti);
5189fb66d81Schristos 
5199fb66d81Schristos 		bd = key_bindings_next(kt, bd);
5209fb66d81Schristos 	}
5219fb66d81Schristos 
5229fb66d81Schristos 	format_free(ft);
5239fb66d81Schristos }
5249fb66d81Schristos 
5259fb66d81Schristos static void
window_customize_build(void * modedata,__unused struct mode_tree_sort_criteria * sort_crit,__unused uint64_t * tag,const char * filter)5269fb66d81Schristos window_customize_build(void *modedata,
5279fb66d81Schristos     __unused struct mode_tree_sort_criteria *sort_crit, __unused uint64_t *tag,
5289fb66d81Schristos     const char *filter)
5299fb66d81Schristos {
5309fb66d81Schristos 	struct window_customize_modedata	*data = modedata;
5319fb66d81Schristos 	struct cmd_find_state			 fs;
5329fb66d81Schristos 	struct format_tree			*ft;
5339fb66d81Schristos 	u_int					 i;
5349fb66d81Schristos 	struct key_table			*kt;
5359fb66d81Schristos 
5369fb66d81Schristos 	for (i = 0; i < data->item_size; i++)
5379fb66d81Schristos 		window_customize_free_item(data->item_list[i]);
5389fb66d81Schristos 	free(data->item_list);
5399fb66d81Schristos 	data->item_list = NULL;
5409fb66d81Schristos 	data->item_size = 0;
5419fb66d81Schristos 
5429fb66d81Schristos 	if (cmd_find_valid_state(&data->fs))
5439fb66d81Schristos 		cmd_find_copy_state(&fs, &data->fs);
5449fb66d81Schristos 	else
5459fb66d81Schristos 		cmd_find_from_pane(&fs, data->wp, 0);
5469fb66d81Schristos 
5479fb66d81Schristos 	ft = format_create_from_state(NULL, NULL, &fs);
5489fb66d81Schristos 	format_add(ft, "is_option", "1");
5499fb66d81Schristos 	format_add(ft, "is_key", "0");
5509fb66d81Schristos 
5519fb66d81Schristos 	window_customize_build_options(data, "Server Options",
5529fb66d81Schristos 	    (3ULL << 62)|(OPTIONS_TABLE_SERVER << 1)|1,
5539fb66d81Schristos 	    WINDOW_CUSTOMIZE_SERVER, global_options,
5549fb66d81Schristos 	    WINDOW_CUSTOMIZE_NONE, NULL,
5559fb66d81Schristos 	    WINDOW_CUSTOMIZE_NONE, NULL,
5569fb66d81Schristos 	    ft, filter, &fs);
5579fb66d81Schristos 	window_customize_build_options(data, "Session Options",
5589fb66d81Schristos 	    (3ULL << 62)|(OPTIONS_TABLE_SESSION << 1)|1,
5599fb66d81Schristos 	    WINDOW_CUSTOMIZE_GLOBAL_SESSION, global_s_options,
5609fb66d81Schristos 	    WINDOW_CUSTOMIZE_SESSION, fs.s->options,
5619fb66d81Schristos 	    WINDOW_CUSTOMIZE_NONE, NULL,
5629fb66d81Schristos 	    ft, filter, &fs);
5639fb66d81Schristos 	window_customize_build_options(data, "Window & Pane Options",
5649fb66d81Schristos 	    (3ULL << 62)|(OPTIONS_TABLE_WINDOW << 1)|1,
5659fb66d81Schristos 	    WINDOW_CUSTOMIZE_GLOBAL_WINDOW, global_w_options,
5669fb66d81Schristos 	    WINDOW_CUSTOMIZE_WINDOW, fs.w->options,
5679fb66d81Schristos 	    WINDOW_CUSTOMIZE_PANE, fs.wp->options,
5689fb66d81Schristos 	    ft, filter, &fs);
5699fb66d81Schristos 
5709fb66d81Schristos 	format_free(ft);
5719fb66d81Schristos 	ft = format_create_from_state(NULL, NULL, &fs);
5729fb66d81Schristos 
5739fb66d81Schristos 	i = 0;
5749fb66d81Schristos 	kt = key_bindings_first_table();
5759fb66d81Schristos 	while (kt != NULL) {
5769fb66d81Schristos 		if (!RB_EMPTY(&kt->key_bindings)) {
5779fb66d81Schristos 			window_customize_build_keys(data, kt, ft, filter, &fs,
5789fb66d81Schristos 			    i);
5799fb66d81Schristos 			if (++i == 256)
5809fb66d81Schristos 				break;
5819fb66d81Schristos 		}
5829fb66d81Schristos 		kt = key_bindings_next_table(kt);
5839fb66d81Schristos 	}
5849fb66d81Schristos 
5859fb66d81Schristos 	format_free(ft);
5869fb66d81Schristos }
5879fb66d81Schristos 
5889fb66d81Schristos static void
window_customize_draw_key(__unused struct window_customize_modedata * data,struct window_customize_itemdata * item,struct screen_write_ctx * ctx,u_int sx,u_int sy)5899fb66d81Schristos window_customize_draw_key(__unused struct window_customize_modedata *data,
5909fb66d81Schristos     struct window_customize_itemdata *item, struct screen_write_ctx *ctx,
5919fb66d81Schristos     u_int sx, u_int sy)
5929fb66d81Schristos {
5939fb66d81Schristos 	struct screen		*s = ctx->s;
5949fb66d81Schristos 	u_int			 cx = s->cx, cy = s->cy;
5959fb66d81Schristos 	struct key_table	*kt;
5969fb66d81Schristos 	struct key_binding	*bd, *default_bd;
5979fb66d81Schristos 	const char		*note, *period = "";
5989fb66d81Schristos 	char			*cmd, *default_cmd;
5999fb66d81Schristos 
6009fb66d81Schristos 	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
6019fb66d81Schristos 		return;
6029fb66d81Schristos 
6039fb66d81Schristos 	note = bd->note;
6049fb66d81Schristos 	if (note == NULL)
6059fb66d81Schristos 		note = "There is no note for this key.";
6069fb66d81Schristos 	if (*note != '\0' && note[strlen (note) - 1] != '.')
6079fb66d81Schristos 		period = ".";
6089fb66d81Schristos 	if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s%s",
6099fb66d81Schristos 	    note, period))
6109fb66d81Schristos 		return;
6119fb66d81Schristos 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
6129fb66d81Schristos 	if (s->cy >= cy + sy - 1)
6139fb66d81Schristos 		return;
6149fb66d81Schristos 
6159fb66d81Schristos 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
6169fb66d81Schristos 	    &grid_default_cell, "This key is in the %s table.", kt->name))
6179fb66d81Schristos 		return;
6189fb66d81Schristos 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
6199fb66d81Schristos 	    &grid_default_cell, "This key %s repeat.",
6209fb66d81Schristos 	    (bd->flags & KEY_BINDING_REPEAT) ? "does" : "does not"))
6219fb66d81Schristos 		return;
6229fb66d81Schristos 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
6239fb66d81Schristos 	if (s->cy >= cy + sy - 1)
6249fb66d81Schristos 		return;
6259fb66d81Schristos 
6269fb66d81Schristos 	cmd = cmd_list_print(bd->cmdlist, 0);
6279fb66d81Schristos 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
6289fb66d81Schristos 	    &grid_default_cell, "Command: %s", cmd)) {
6299fb66d81Schristos 		free(cmd);
6309fb66d81Schristos 		return;
6319fb66d81Schristos 	}
6329fb66d81Schristos 	default_bd = key_bindings_get_default(kt, bd->key);
6339fb66d81Schristos 	if (default_bd != NULL) {
6349fb66d81Schristos 		default_cmd = cmd_list_print(default_bd->cmdlist, 0);
6359fb66d81Schristos 		if (strcmp(cmd, default_cmd) != 0 &&
6369fb66d81Schristos 		    !screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
6379fb66d81Schristos 		    &grid_default_cell, "The default is: %s", default_cmd)) {
6389fb66d81Schristos 			free(default_cmd);
6399fb66d81Schristos 			free(cmd);
6409fb66d81Schristos 			return;
6419fb66d81Schristos 		}
6429fb66d81Schristos 		free(default_cmd);
6439fb66d81Schristos 	}
6449fb66d81Schristos 	free(cmd);
6459fb66d81Schristos }
6469fb66d81Schristos 
6479fb66d81Schristos static void
window_customize_draw_option(struct window_customize_modedata * data,struct window_customize_itemdata * item,struct screen_write_ctx * ctx,u_int sx,u_int sy)6489fb66d81Schristos window_customize_draw_option(struct window_customize_modedata *data,
6499fb66d81Schristos     struct window_customize_itemdata *item, struct screen_write_ctx *ctx,
6509fb66d81Schristos     u_int sx, u_int sy)
6519fb66d81Schristos {
6529fb66d81Schristos 	struct screen				 *s = ctx->s;
6539fb66d81Schristos 	u_int					  cx = s->cx, cy = s->cy;
6549fb66d81Schristos 	int					  idx;
6559fb66d81Schristos 	struct options_entry			 *o, *parent;
6569fb66d81Schristos 	struct options				 *go, *wo;
6579fb66d81Schristos 	const struct options_table_entry	 *oe;
6589fb66d81Schristos 	struct grid_cell			  gc;
6599fb66d81Schristos 	const char				**choice, *text, *name;
6609fb66d81Schristos 	const char				 *space = "", *unit = "";
6619fb66d81Schristos 	char					 *value = NULL, *expanded;
6629fb66d81Schristos 	char					 *default_value = NULL;
6639fb66d81Schristos 	char					  choices[256] = "";
6649fb66d81Schristos 	struct cmd_find_state			  fs;
6659fb66d81Schristos 	struct format_tree			 *ft;
6669fb66d81Schristos 
6679fb66d81Schristos 	if (!window_customize_check_item(data, item, &fs))
6689fb66d81Schristos 		return;
6699fb66d81Schristos 	name = item->name;
6709fb66d81Schristos 	idx = item->idx;
6719fb66d81Schristos 
6729fb66d81Schristos 	o = options_get(item->oo, name);
6739fb66d81Schristos 	if (o == NULL)
6749fb66d81Schristos 		return;
6759fb66d81Schristos 	oe = options_table_entry(o);
6769fb66d81Schristos 
6779fb66d81Schristos 	if (oe != NULL && oe->unit != NULL) {
6789fb66d81Schristos 		space = " ";
6799fb66d81Schristos 		unit = oe->unit;
6809fb66d81Schristos 	}
6819fb66d81Schristos 	ft = format_create_from_state(NULL, NULL, &fs);
6829fb66d81Schristos 
683*46548964Swiz 	if (oe == NULL || oe->text == NULL)
6849fb66d81Schristos 		text = "This option doesn't have a description.";
6859fb66d81Schristos 	else
6869fb66d81Schristos 		text = oe->text;
6879fb66d81Schristos 	if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s",
6889fb66d81Schristos 	    text))
6899fb66d81Schristos 		goto out;
6909fb66d81Schristos 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
6919fb66d81Schristos 	if (s->cy >= cy + sy - 1)
6929fb66d81Schristos 		goto out;
6939fb66d81Schristos 
6949fb66d81Schristos 	if (oe == NULL)
6959fb66d81Schristos 		text = "user";
6969fb66d81Schristos 	else if ((oe->scope & (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) ==
6979fb66d81Schristos 	    (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE))
6989fb66d81Schristos 		text = "window and pane";
6999fb66d81Schristos 	else if (oe->scope & OPTIONS_TABLE_WINDOW)
7009fb66d81Schristos 		text = "window";
7019fb66d81Schristos 	else if (oe->scope & OPTIONS_TABLE_SESSION)
7029fb66d81Schristos 		text = "session";
7039fb66d81Schristos 	else
7049fb66d81Schristos 		text = "server";
7059fb66d81Schristos 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
7069fb66d81Schristos 	    &grid_default_cell, "This is a %s option.", text))
7079fb66d81Schristos 		goto out;
7089fb66d81Schristos 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
7099fb66d81Schristos 		if (idx != -1) {
7109fb66d81Schristos 			if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
7119fb66d81Schristos 			    0, &grid_default_cell,
7129fb66d81Schristos 			    "This is an array option, index %u.", idx))
7139fb66d81Schristos 				goto out;
7149fb66d81Schristos 		} else {
7159fb66d81Schristos 			if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
7169fb66d81Schristos 			    0, &grid_default_cell, "This is an array option."))
7179fb66d81Schristos 				goto out;
7189fb66d81Schristos 		}
7199fb66d81Schristos 		if (idx == -1)
7209fb66d81Schristos 			goto out;
7219fb66d81Schristos 	}
7229fb66d81Schristos 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
7239fb66d81Schristos 	if (s->cy >= cy + sy - 1)
7249fb66d81Schristos 		goto out;
7259fb66d81Schristos 
7269fb66d81Schristos 	value = options_to_string(o, idx, 0);
7279fb66d81Schristos 	if (oe != NULL && idx == -1) {
7289fb66d81Schristos 		default_value = options_default_to_string(oe);
7299fb66d81Schristos 		if (strcmp(default_value, value) == 0) {
7309fb66d81Schristos 			free(default_value);
7319fb66d81Schristos 			default_value = NULL;
7329fb66d81Schristos 		}
7339fb66d81Schristos 	}
7349fb66d81Schristos 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
7359fb66d81Schristos 	    &grid_default_cell, "Option value: %s%s%s", value, space, unit))
7369fb66d81Schristos 		goto out;
7379fb66d81Schristos 	if (oe == NULL || oe->type == OPTIONS_TABLE_STRING) {
7389fb66d81Schristos 		expanded = format_expand(ft, value);
7399fb66d81Schristos 		if (strcmp(expanded, value) != 0) {
7409fb66d81Schristos 			if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
7419fb66d81Schristos 			    0, &grid_default_cell, "This expands to: %s",
7429fb66d81Schristos 			    expanded))
7439fb66d81Schristos 				goto out;
7449fb66d81Schristos 		}
7459fb66d81Schristos 		free(expanded);
7469fb66d81Schristos 	}
7479fb66d81Schristos 	if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) {
7489fb66d81Schristos 		for (choice = oe->choices; *choice != NULL; choice++) {
7499fb66d81Schristos 			strlcat(choices, *choice, sizeof choices);
7509fb66d81Schristos 			strlcat(choices, ", ", sizeof choices);
7519fb66d81Schristos 		}
7529fb66d81Schristos 		choices[strlen(choices) - 2] = '\0';
7539fb66d81Schristos 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
7549fb66d81Schristos 		    &grid_default_cell, "Available values are: %s",
7559fb66d81Schristos 		    choices))
7569fb66d81Schristos 			goto out;
7579fb66d81Schristos 	}
7589fb66d81Schristos 	if (oe != NULL && oe->type == OPTIONS_TABLE_COLOUR) {
7599fb66d81Schristos 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
7609fb66d81Schristos 		    &grid_default_cell, "This is a colour option: "))
7619fb66d81Schristos 			goto out;
7629fb66d81Schristos 		memcpy(&gc, &grid_default_cell, sizeof gc);
7639fb66d81Schristos 		gc.fg = options_get_number(item->oo, name);
7649fb66d81Schristos 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
7659fb66d81Schristos 		    "EXAMPLE"))
7669fb66d81Schristos 			goto out;
7679fb66d81Schristos 	}
7689fb66d81Schristos 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_STYLE)) {
7699fb66d81Schristos 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
7709fb66d81Schristos 		    &grid_default_cell, "This is a style option: "))
7719fb66d81Schristos 			goto out;
7729fb66d81Schristos 		style_apply(&gc, item->oo, name, ft);
7739fb66d81Schristos 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
7749fb66d81Schristos 		    "EXAMPLE"))
7759fb66d81Schristos 			goto out;
7769fb66d81Schristos 	}
7779fb66d81Schristos 	if (default_value != NULL) {
7789fb66d81Schristos 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
7799fb66d81Schristos 		    &grid_default_cell, "The default is: %s%s%s", default_value,
7809fb66d81Schristos 		    space, unit))
7819fb66d81Schristos 			goto out;
7829fb66d81Schristos 	}
7839fb66d81Schristos 
7849fb66d81Schristos 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
7859fb66d81Schristos 	if (s->cy > cy + sy - 1)
7869fb66d81Schristos 		goto out;
7879fb66d81Schristos 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
7889fb66d81Schristos 		wo = NULL;
7899fb66d81Schristos 		go = NULL;
7909fb66d81Schristos 	} else {
7919fb66d81Schristos 		switch (item->scope) {
7929fb66d81Schristos 		case WINDOW_CUSTOMIZE_PANE:
7939fb66d81Schristos 			wo = options_get_parent(item->oo);
7949fb66d81Schristos 			go = options_get_parent(wo);
7959fb66d81Schristos 			break;
7969fb66d81Schristos 		case WINDOW_CUSTOMIZE_WINDOW:
7979fb66d81Schristos 		case WINDOW_CUSTOMIZE_SESSION:
7989fb66d81Schristos 			wo = NULL;
7999fb66d81Schristos 			go = options_get_parent(item->oo);
8009fb66d81Schristos 			break;
8019fb66d81Schristos 		default:
8029fb66d81Schristos 			wo = NULL;
8039fb66d81Schristos 			go = NULL;
8049fb66d81Schristos 			break;
8059fb66d81Schristos 		}
8069fb66d81Schristos 	}
8079fb66d81Schristos 	if (wo != NULL && options_owner(o) != wo) {
8089fb66d81Schristos 		parent = options_get_only(wo, name);
8099fb66d81Schristos 		if (parent != NULL) {
8109fb66d81Schristos 			value = options_to_string(parent, -1 , 0);
8119fb66d81Schristos 			if (!screen_write_text(ctx, s->cx, sx,
8129fb66d81Schristos 			    sy - (s->cy - cy), 0, &grid_default_cell,
8139fb66d81Schristos 			    "Window value (from window %u): %s%s%s", fs.wl->idx,
8149fb66d81Schristos 			    value, space, unit))
8159fb66d81Schristos 				goto out;
8169fb66d81Schristos 		}
8179fb66d81Schristos 	}
8189fb66d81Schristos 	if (go != NULL && options_owner(o) != go) {
8199fb66d81Schristos 		parent = options_get_only(go, name);
8209fb66d81Schristos 		if (parent != NULL) {
8219fb66d81Schristos 			value = options_to_string(parent, -1 , 0);
8229fb66d81Schristos 			if (!screen_write_text(ctx, s->cx, sx,
8239fb66d81Schristos 			    sy - (s->cy - cy), 0, &grid_default_cell,
8249fb66d81Schristos 			    "Global value: %s%s%s", value, space, unit))
8259fb66d81Schristos 				goto out;
8269fb66d81Schristos 		}
8279fb66d81Schristos 	}
8289fb66d81Schristos 
8299fb66d81Schristos out:
8309fb66d81Schristos 	free(value);
8319fb66d81Schristos 	free(default_value);
8329fb66d81Schristos 	format_free(ft);
8339fb66d81Schristos }
8349fb66d81Schristos 
8359fb66d81Schristos static void
window_customize_draw(void * modedata,void * itemdata,struct screen_write_ctx * ctx,u_int sx,u_int sy)8369fb66d81Schristos window_customize_draw(void *modedata, void *itemdata,
8379fb66d81Schristos     struct screen_write_ctx *ctx, u_int sx, u_int sy)
8389fb66d81Schristos {
8399fb66d81Schristos 	struct window_customize_modedata	*data = modedata;
8409fb66d81Schristos 	struct window_customize_itemdata	*item = itemdata;
8419fb66d81Schristos 
8429fb66d81Schristos 	if (item == NULL)
8439fb66d81Schristos 		return;
8449fb66d81Schristos 
8459fb66d81Schristos 	if (item->scope == WINDOW_CUSTOMIZE_KEY)
8469fb66d81Schristos 		window_customize_draw_key(data, item, ctx, sx, sy);
8479fb66d81Schristos 	else
8489fb66d81Schristos 		window_customize_draw_option(data, item, ctx, sx, sy);
8499fb66d81Schristos }
8509fb66d81Schristos 
8519fb66d81Schristos static void
window_customize_menu(void * modedata,struct client * c,key_code key)8529fb66d81Schristos window_customize_menu(void *modedata, struct client *c, key_code key)
8539fb66d81Schristos {
8549fb66d81Schristos 	struct window_customize_modedata	*data = modedata;
8559fb66d81Schristos 	struct window_pane			*wp = data->wp;
8569fb66d81Schristos 	struct window_mode_entry		*wme;
8579fb66d81Schristos 
8589fb66d81Schristos 	wme = TAILQ_FIRST(&wp->modes);
8599fb66d81Schristos 	if (wme == NULL || wme->data != modedata)
8609fb66d81Schristos 		return;
8619fb66d81Schristos 	window_customize_key(wme, c, NULL, NULL, key, NULL);
8629fb66d81Schristos }
8639fb66d81Schristos 
8649fb66d81Schristos static u_int
window_customize_height(__unused void * modedata,__unused u_int height)8659fb66d81Schristos window_customize_height(__unused void *modedata, __unused u_int height)
8669fb66d81Schristos {
8679fb66d81Schristos 	return (12);
8689fb66d81Schristos }
8699fb66d81Schristos 
8709fb66d81Schristos static struct screen *
window_customize_init(struct window_mode_entry * wme,struct cmd_find_state * fs,struct args * args)8719fb66d81Schristos window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
8729fb66d81Schristos     struct args *args)
8739fb66d81Schristos {
8749fb66d81Schristos 	struct window_pane			*wp = wme->wp;
8759fb66d81Schristos 	struct window_customize_modedata	*data;
8769fb66d81Schristos 	struct screen				*s;
8779fb66d81Schristos 
8789fb66d81Schristos 	wme->data = data = xcalloc(1, sizeof *data);
8799fb66d81Schristos 	data->wp = wp;
8809fb66d81Schristos 	data->references = 1;
8819fb66d81Schristos 
8829fb66d81Schristos 	memcpy(&data->fs, fs, sizeof data->fs);
8839fb66d81Schristos 
8849fb66d81Schristos 	if (args == NULL || !args_has(args, 'F'))
8859fb66d81Schristos 		data->format = xstrdup(WINDOW_CUSTOMIZE_DEFAULT_FORMAT);
8869fb66d81Schristos 	else
8879fb66d81Schristos 		data->format = xstrdup(args_get(args, 'F'));
8889fb66d81Schristos 
8899fb66d81Schristos 	data->data = mode_tree_start(wp, args, window_customize_build,
8909fb66d81Schristos 	    window_customize_draw, NULL, window_customize_menu,
8919fb66d81Schristos 	    window_customize_height, NULL, data, window_customize_menu_items,
8929fb66d81Schristos 	    NULL, 0, &s);
8939fb66d81Schristos 	mode_tree_zoom(data->data, args);
8949fb66d81Schristos 
8959fb66d81Schristos 	mode_tree_build(data->data);
8969fb66d81Schristos 	mode_tree_draw(data->data);
8979fb66d81Schristos 
8989fb66d81Schristos 	return (s);
8999fb66d81Schristos }
9009fb66d81Schristos 
9019fb66d81Schristos static void
window_customize_destroy(struct window_customize_modedata * data)9029fb66d81Schristos window_customize_destroy(struct window_customize_modedata *data)
9039fb66d81Schristos {
9049fb66d81Schristos 	u_int	i;
9059fb66d81Schristos 
9069fb66d81Schristos 	if (--data->references != 0)
9079fb66d81Schristos 		return;
9089fb66d81Schristos 
9099fb66d81Schristos 	for (i = 0; i < data->item_size; i++)
9109fb66d81Schristos 		window_customize_free_item(data->item_list[i]);
9119fb66d81Schristos 	free(data->item_list);
9129fb66d81Schristos 
9139fb66d81Schristos 	free(data->format);
9149fb66d81Schristos 
9159fb66d81Schristos 	free(data);
9169fb66d81Schristos }
9179fb66d81Schristos 
9189fb66d81Schristos static void
window_customize_free(struct window_mode_entry * wme)9199fb66d81Schristos window_customize_free(struct window_mode_entry *wme)
9209fb66d81Schristos {
9219fb66d81Schristos 	struct window_customize_modedata *data = wme->data;
9229fb66d81Schristos 
9239fb66d81Schristos 	if (data == NULL)
9249fb66d81Schristos 		return;
9259fb66d81Schristos 
9269fb66d81Schristos 	data->dead = 1;
9279fb66d81Schristos 	mode_tree_free(data->data);
9289fb66d81Schristos 	window_customize_destroy(data);
9299fb66d81Schristos }
9309fb66d81Schristos 
9319fb66d81Schristos static void
window_customize_resize(struct window_mode_entry * wme,u_int sx,u_int sy)9329fb66d81Schristos window_customize_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
9339fb66d81Schristos {
9349fb66d81Schristos 	struct window_customize_modedata	*data = wme->data;
9359fb66d81Schristos 
9369fb66d81Schristos 	mode_tree_resize(data->data, sx, sy);
9379fb66d81Schristos }
9389fb66d81Schristos 
9399fb66d81Schristos static void
window_customize_free_callback(void * modedata)9409fb66d81Schristos window_customize_free_callback(void *modedata)
9419fb66d81Schristos {
9429fb66d81Schristos 	window_customize_destroy(modedata);
9439fb66d81Schristos }
9449fb66d81Schristos 
9459fb66d81Schristos static void
window_customize_free_item_callback(void * itemdata)9469fb66d81Schristos window_customize_free_item_callback(void *itemdata)
9479fb66d81Schristos {
9489fb66d81Schristos 	struct window_customize_itemdata	*item = itemdata;
9499fb66d81Schristos 	struct window_customize_modedata	*data = item->data;
9509fb66d81Schristos 
9519fb66d81Schristos 	window_customize_free_item(item);
9529fb66d81Schristos 	window_customize_destroy(data);
9539fb66d81Schristos }
9549fb66d81Schristos 
9559fb66d81Schristos static int
window_customize_set_option_callback(struct client * c,void * itemdata,const char * s,__unused int done)9569fb66d81Schristos window_customize_set_option_callback(struct client *c, void *itemdata,
9579fb66d81Schristos     const char *s, __unused int done)
9589fb66d81Schristos {
9599fb66d81Schristos 	struct window_customize_itemdata	*item = itemdata;
9609fb66d81Schristos 	struct window_customize_modedata	*data = item->data;
9619fb66d81Schristos 	struct options_entry			*o;
9629fb66d81Schristos 	const struct options_table_entry	*oe;
9639fb66d81Schristos 	struct options				*oo = item->oo;
9649fb66d81Schristos 	const char				*name = item->name;
9659fb66d81Schristos 	char					*cause;
9669fb66d81Schristos 	int					 idx = item->idx;
9679fb66d81Schristos 
9689fb66d81Schristos 	if (s == NULL || *s == '\0' || data->dead)
9699fb66d81Schristos 		return (0);
9709fb66d81Schristos 	if (item == NULL || !window_customize_check_item(data, item, NULL))
9719fb66d81Schristos 		return (0);
9729fb66d81Schristos 	o = options_get(oo, name);
9739fb66d81Schristos 	if (o == NULL)
9749fb66d81Schristos 		return (0);
9759fb66d81Schristos 	oe = options_table_entry(o);
9769fb66d81Schristos 
9779fb66d81Schristos 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
9789fb66d81Schristos 		if (idx == -1) {
9799fb66d81Schristos 			for (idx = 0; idx < INT_MAX; idx++) {
9809fb66d81Schristos 				if (options_array_get(o, idx) == NULL)
9819fb66d81Schristos 					break;
9829fb66d81Schristos 			}
9839fb66d81Schristos 		}
9849fb66d81Schristos 		if (options_array_set(o, idx, s, 0, &cause) != 0)
9859fb66d81Schristos 			goto fail;
9869fb66d81Schristos 	} else {
9879fb66d81Schristos 		if (options_from_string(oo, oe, name, s, 0, &cause) != 0)
9889fb66d81Schristos 			goto fail;
9899fb66d81Schristos 	}
9909fb66d81Schristos 
9919fb66d81Schristos 	options_push_changes(item->name);
9929fb66d81Schristos 	mode_tree_build(data->data);
9939fb66d81Schristos 	mode_tree_draw(data->data);
9949fb66d81Schristos 	data->wp->flags |= PANE_REDRAW;
9959fb66d81Schristos 
9969fb66d81Schristos 	return (0);
9979fb66d81Schristos 
9989fb66d81Schristos fail:
9999fb66d81Schristos 	*cause = toupper((u_char)*cause);
10009fb66d81Schristos 	status_message_set(c, -1, 1, 0, "%s", cause);
10019fb66d81Schristos 	free(cause);
10029fb66d81Schristos 	return (0);
10039fb66d81Schristos }
10049fb66d81Schristos 
10059fb66d81Schristos static void
window_customize_set_option(struct client * c,struct window_customize_modedata * data,struct window_customize_itemdata * item,int global,int pane)10069fb66d81Schristos window_customize_set_option(struct client *c,
10079fb66d81Schristos     struct window_customize_modedata *data,
10089fb66d81Schristos     struct window_customize_itemdata *item, int global, int pane)
10099fb66d81Schristos {
10109fb66d81Schristos 	struct options_entry			*o;
10119fb66d81Schristos 	const struct options_table_entry	*oe;
10129fb66d81Schristos 	struct options				*oo;
10139fb66d81Schristos 	struct window_customize_itemdata	*new_item;
10149fb66d81Schristos 	int					 flag, idx = item->idx;
10159fb66d81Schristos 	enum window_customize_scope		 scope = WINDOW_CUSTOMIZE_NONE;
10169fb66d81Schristos 	u_int					 choice;
10179fb66d81Schristos 	const char				*name = item->name, *space = "";
10189fb66d81Schristos 	char					*prompt, *value, *text;
10199fb66d81Schristos 	struct cmd_find_state			 fs;
10209fb66d81Schristos 
10219fb66d81Schristos 	if (item == NULL || !window_customize_check_item(data, item, &fs))
10229fb66d81Schristos 		return;
10239fb66d81Schristos 	o = options_get(item->oo, name);
10249fb66d81Schristos 	if (o == NULL)
10259fb66d81Schristos 		return;
10269fb66d81Schristos 
10279fb66d81Schristos 	oe = options_table_entry(o);
10289fb66d81Schristos 	if (oe != NULL && ~oe->scope & OPTIONS_TABLE_PANE)
10299fb66d81Schristos 		pane = 0;
10309fb66d81Schristos 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
10319fb66d81Schristos 		scope = item->scope;
10329fb66d81Schristos 		oo = item->oo;
10339fb66d81Schristos 	} else {
10349fb66d81Schristos 		if (global) {
10359fb66d81Schristos 			switch (item->scope) {
10369fb66d81Schristos 			case WINDOW_CUSTOMIZE_NONE:
10379fb66d81Schristos 			case WINDOW_CUSTOMIZE_KEY:
10389fb66d81Schristos 			case WINDOW_CUSTOMIZE_SERVER:
10399fb66d81Schristos 			case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
10409fb66d81Schristos 			case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
10419fb66d81Schristos 				scope = item->scope;
10429fb66d81Schristos 				break;
10439fb66d81Schristos 			case WINDOW_CUSTOMIZE_SESSION:
10449fb66d81Schristos 				scope = WINDOW_CUSTOMIZE_GLOBAL_SESSION;
10459fb66d81Schristos 				break;
10469fb66d81Schristos 			case WINDOW_CUSTOMIZE_WINDOW:
10479fb66d81Schristos 			case WINDOW_CUSTOMIZE_PANE:
10489fb66d81Schristos 				scope = WINDOW_CUSTOMIZE_GLOBAL_WINDOW;
10499fb66d81Schristos 				break;
10509fb66d81Schristos 			}
10519fb66d81Schristos 		} else {
10529fb66d81Schristos 			switch (item->scope) {
10539fb66d81Schristos 			case WINDOW_CUSTOMIZE_NONE:
10549fb66d81Schristos 			case WINDOW_CUSTOMIZE_KEY:
10559fb66d81Schristos 			case WINDOW_CUSTOMIZE_SERVER:
10569fb66d81Schristos 			case WINDOW_CUSTOMIZE_SESSION:
10579fb66d81Schristos 				scope = item->scope;
10589fb66d81Schristos 				break;
10599fb66d81Schristos 			case WINDOW_CUSTOMIZE_WINDOW:
10609fb66d81Schristos 			case WINDOW_CUSTOMIZE_PANE:
10619fb66d81Schristos 				if (pane)
10629fb66d81Schristos 					scope = WINDOW_CUSTOMIZE_PANE;
10639fb66d81Schristos 				else
10649fb66d81Schristos 					scope = WINDOW_CUSTOMIZE_WINDOW;
10659fb66d81Schristos 				break;
10669fb66d81Schristos 			case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
10679fb66d81Schristos 				scope = WINDOW_CUSTOMIZE_SESSION;
10689fb66d81Schristos 				break;
10699fb66d81Schristos 			case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
10709fb66d81Schristos 				if (pane)
10719fb66d81Schristos 					scope = WINDOW_CUSTOMIZE_PANE;
10729fb66d81Schristos 				else
10739fb66d81Schristos 					scope = WINDOW_CUSTOMIZE_WINDOW;
10749fb66d81Schristos 				break;
10759fb66d81Schristos 			}
10769fb66d81Schristos 		}
10779fb66d81Schristos 		if (scope == item->scope)
10789fb66d81Schristos 			oo = item->oo;
10799fb66d81Schristos 		else
10809fb66d81Schristos 			oo = window_customize_get_tree(scope, &fs);
10819fb66d81Schristos 	}
10829fb66d81Schristos 
10839fb66d81Schristos 	if (oe != NULL && oe->type == OPTIONS_TABLE_FLAG) {
10849fb66d81Schristos 		flag = options_get_number(oo, name);
10859fb66d81Schristos 		options_set_number(oo, name, !flag);
10869fb66d81Schristos 	} else if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) {
10879fb66d81Schristos 		choice = options_get_number(oo, name);
10889fb66d81Schristos 		if (oe->choices[choice + 1] == NULL)
10899fb66d81Schristos 			choice = 0;
10909fb66d81Schristos 		else
10919fb66d81Schristos 			choice++;
10929fb66d81Schristos 		options_set_number(oo, name, choice);
10939fb66d81Schristos 	} else {
10949fb66d81Schristos 		text = window_customize_scope_text(scope, &fs);
10959fb66d81Schristos 		if (*text != '\0')
10969fb66d81Schristos 			space = ", for ";
10979fb66d81Schristos 		else if (scope != WINDOW_CUSTOMIZE_SERVER)
10989fb66d81Schristos 			space = ", global";
10999fb66d81Schristos 		if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
11009fb66d81Schristos 			if (idx == -1) {
11019fb66d81Schristos 				xasprintf(&prompt, "(%s[+]%s%s) ", name, space,
11029fb66d81Schristos 				    text);
11039fb66d81Schristos 			} else {
11049fb66d81Schristos 				xasprintf(&prompt, "(%s[%d]%s%s) ", name, idx,
11059fb66d81Schristos 				    space, text);
11069fb66d81Schristos 			}
11079fb66d81Schristos 		} else
11089fb66d81Schristos 			xasprintf(&prompt, "(%s%s%s) ", name, space, text);
11099fb66d81Schristos 		free(text);
11109fb66d81Schristos 
11119fb66d81Schristos 		value = options_to_string(o, idx, 0);
11129fb66d81Schristos 
11139fb66d81Schristos 		new_item = xcalloc(1, sizeof *new_item);
11149fb66d81Schristos 		new_item->data = data;
11159fb66d81Schristos 		new_item->scope = scope;
11169fb66d81Schristos 		new_item->oo = oo;
11179fb66d81Schristos 		new_item->name = xstrdup(name);
11189fb66d81Schristos 		new_item->idx = idx;
11199fb66d81Schristos 
11209fb66d81Schristos 		data->references++;
11219fb66d81Schristos 		status_prompt_set(c, NULL, prompt, value,
11229fb66d81Schristos 		    window_customize_set_option_callback,
11239fb66d81Schristos 		    window_customize_free_item_callback, new_item,
1124*46548964Swiz 		    PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
11259fb66d81Schristos 
11269fb66d81Schristos 		free(prompt);
11279fb66d81Schristos 		free(value);
11289fb66d81Schristos 	}
11299fb66d81Schristos }
11309fb66d81Schristos 
11319fb66d81Schristos static void
window_customize_unset_option(struct window_customize_modedata * data,struct window_customize_itemdata * item)11329fb66d81Schristos window_customize_unset_option(struct window_customize_modedata *data,
11339fb66d81Schristos     struct window_customize_itemdata *item)
11349fb66d81Schristos {
11359fb66d81Schristos 	struct options_entry	*o;
11369fb66d81Schristos 
11379fb66d81Schristos 	if (item == NULL || !window_customize_check_item(data, item, NULL))
11389fb66d81Schristos 		return;
11399fb66d81Schristos 
11409fb66d81Schristos 	o = options_get(item->oo, item->name);
11419fb66d81Schristos 	if (o == NULL)
11429fb66d81Schristos 		return;
11439fb66d81Schristos 	if (item->idx != -1 && item == mode_tree_get_current(data->data))
11449fb66d81Schristos 		mode_tree_up(data->data, 0);
11459fb66d81Schristos 	options_remove_or_default(o, item->idx, NULL);
11469fb66d81Schristos }
11479fb66d81Schristos 
11489fb66d81Schristos static void
window_customize_reset_option(struct window_customize_modedata * data,struct window_customize_itemdata * item)11499fb66d81Schristos window_customize_reset_option(struct window_customize_modedata *data,
11509fb66d81Schristos     struct window_customize_itemdata *item)
11519fb66d81Schristos {
11529fb66d81Schristos 	struct options		*oo;
11539fb66d81Schristos 	struct options_entry	*o;
11549fb66d81Schristos 
11559fb66d81Schristos 	if (item == NULL || !window_customize_check_item(data, item, NULL))
11569fb66d81Schristos 		return;
11579fb66d81Schristos 	if (item->idx != -1)
11589fb66d81Schristos 		return;
11599fb66d81Schristos 
11609fb66d81Schristos 	oo = item->oo;
11619fb66d81Schristos 	while (oo != NULL) {
11629fb66d81Schristos 		o = options_get_only(item->oo, item->name);
11639fb66d81Schristos 		if (o != NULL)
11649fb66d81Schristos 			options_remove_or_default(o, -1, NULL);
11659fb66d81Schristos 		oo = options_get_parent(oo);
11669fb66d81Schristos 	}
11679fb66d81Schristos }
11689fb66d81Schristos 
11699fb66d81Schristos static int
window_customize_set_command_callback(struct client * c,void * itemdata,const char * s,__unused int done)11709fb66d81Schristos window_customize_set_command_callback(struct client *c, void *itemdata,
11719fb66d81Schristos     const char *s, __unused int done)
11729fb66d81Schristos {
11739fb66d81Schristos 	struct window_customize_itemdata	*item = itemdata;
11749fb66d81Schristos 	struct window_customize_modedata	*data = item->data;
11759fb66d81Schristos 	struct key_binding			*bd;
11769fb66d81Schristos 	struct cmd_parse_result			*pr;
11779fb66d81Schristos 	char					*error;
11789fb66d81Schristos 
11799fb66d81Schristos 	if (s == NULL || *s == '\0' || data->dead)
11809fb66d81Schristos 		return (0);
11819fb66d81Schristos 	if (item == NULL || !window_customize_get_key(item, NULL, &bd))
11829fb66d81Schristos 		return (0);
11839fb66d81Schristos 
11849fb66d81Schristos 	pr = cmd_parse_from_string(s, NULL);
11859fb66d81Schristos 	switch (pr->status) {
11869fb66d81Schristos 	case CMD_PARSE_ERROR:
11879fb66d81Schristos 		error = pr->error;
11889fb66d81Schristos 		goto fail;
11899fb66d81Schristos 	case CMD_PARSE_SUCCESS:
11909fb66d81Schristos 		break;
11919fb66d81Schristos 	}
11929fb66d81Schristos 	cmd_list_free(bd->cmdlist);
11939fb66d81Schristos 	bd->cmdlist = pr->cmdlist;
11949fb66d81Schristos 
11959fb66d81Schristos 	mode_tree_build(data->data);
11969fb66d81Schristos 	mode_tree_draw(data->data);
11979fb66d81Schristos 	data->wp->flags |= PANE_REDRAW;
11989fb66d81Schristos 
11999fb66d81Schristos 	return (0);
12009fb66d81Schristos 
12019fb66d81Schristos fail:
12029fb66d81Schristos 	*error = toupper((u_char)*error);
12039fb66d81Schristos 	status_message_set(c, -1, 1, 0, "%s", error);
12049fb66d81Schristos 	free(error);
12059fb66d81Schristos 	return (0);
12069fb66d81Schristos }
12079fb66d81Schristos 
12089fb66d81Schristos static int
window_customize_set_note_callback(__unused struct client * c,void * itemdata,const char * s,__unused int done)12099fb66d81Schristos window_customize_set_note_callback(__unused struct client *c, void *itemdata,
12109fb66d81Schristos     const char *s, __unused int done)
12119fb66d81Schristos {
12129fb66d81Schristos 	struct window_customize_itemdata	*item = itemdata;
12139fb66d81Schristos 	struct window_customize_modedata	*data = item->data;
12149fb66d81Schristos 	struct key_binding			*bd;
12159fb66d81Schristos 
12169fb66d81Schristos 	if (s == NULL || *s == '\0' || data->dead)
12179fb66d81Schristos 		return (0);
12189fb66d81Schristos 	if (item == NULL || !window_customize_get_key(item, NULL, &bd))
12199fb66d81Schristos 		return (0);
12209fb66d81Schristos 
1221e271dbb8Schristos 	free(__UNCONST(bd->note));
12229fb66d81Schristos 	bd->note = xstrdup(s);
12239fb66d81Schristos 
12249fb66d81Schristos 	mode_tree_build(data->data);
12259fb66d81Schristos 	mode_tree_draw(data->data);
12269fb66d81Schristos 	data->wp->flags |= PANE_REDRAW;
12279fb66d81Schristos 
12289fb66d81Schristos 	return (0);
12299fb66d81Schristos }
12309fb66d81Schristos 
12319fb66d81Schristos static void
window_customize_set_key(struct client * c,struct window_customize_modedata * data,struct window_customize_itemdata * item)12329fb66d81Schristos window_customize_set_key(struct client *c,
12339fb66d81Schristos     struct window_customize_modedata *data,
12349fb66d81Schristos     struct window_customize_itemdata *item)
12359fb66d81Schristos {
12369fb66d81Schristos 	key_code				 key = item->key;
12379fb66d81Schristos 	struct key_binding			*bd;
12389fb66d81Schristos 	const char				*s;
12399fb66d81Schristos 	char					*prompt, *value;
12409fb66d81Schristos 	struct window_customize_itemdata	*new_item;
12419fb66d81Schristos 
12429fb66d81Schristos 	if (item == NULL || !window_customize_get_key(item, NULL, &bd))
12439fb66d81Schristos 		return;
12449fb66d81Schristos 
12459fb66d81Schristos 	s = mode_tree_get_current_name(data->data);
12469fb66d81Schristos 	if (strcmp(s, "Repeat") == 0)
12479fb66d81Schristos 		bd->flags ^= KEY_BINDING_REPEAT;
12489fb66d81Schristos 	else if (strcmp(s, "Command") == 0) {
12499fb66d81Schristos 		xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0));
12509fb66d81Schristos 		value = cmd_list_print(bd->cmdlist, 0);
12519fb66d81Schristos 
12529fb66d81Schristos 		new_item = xcalloc(1, sizeof *new_item);
12539fb66d81Schristos 		new_item->data = data;
12549fb66d81Schristos 		new_item->scope = item->scope;
12559fb66d81Schristos 		new_item->table = xstrdup(item->table);
12569fb66d81Schristos 		new_item->key = key;
12579fb66d81Schristos 
12589fb66d81Schristos 		data->references++;
12599fb66d81Schristos 		status_prompt_set(c, NULL, prompt, value,
12609fb66d81Schristos 		    window_customize_set_command_callback,
12619fb66d81Schristos 		    window_customize_free_item_callback, new_item,
1262*46548964Swiz 		    PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
12639fb66d81Schristos 		free(prompt);
12649fb66d81Schristos 		free(value);
12659fb66d81Schristos 	} else if (strcmp(s, "Note") == 0) {
12669fb66d81Schristos 		xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0));
12679fb66d81Schristos 
12689fb66d81Schristos 		new_item = xcalloc(1, sizeof *new_item);
12699fb66d81Schristos 		new_item->data = data;
12709fb66d81Schristos 		new_item->scope = item->scope;
12719fb66d81Schristos 		new_item->table = xstrdup(item->table);
12729fb66d81Schristos 		new_item->key = key;
12739fb66d81Schristos 
12749fb66d81Schristos 		data->references++;
12759fb66d81Schristos 		status_prompt_set(c, NULL, prompt,
12769fb66d81Schristos 		    (bd->note == NULL ? "" : bd->note),
12779fb66d81Schristos 		    window_customize_set_note_callback,
12789fb66d81Schristos 		    window_customize_free_item_callback, new_item,
1279*46548964Swiz 		    PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
12809fb66d81Schristos 		free(prompt);
12819fb66d81Schristos 	}
12829fb66d81Schristos }
12839fb66d81Schristos 
12849fb66d81Schristos static void
window_customize_unset_key(struct window_customize_modedata * data,struct window_customize_itemdata * item)12859fb66d81Schristos window_customize_unset_key(struct window_customize_modedata *data,
12869fb66d81Schristos     struct window_customize_itemdata *item)
12879fb66d81Schristos {
12889fb66d81Schristos 	struct key_table	*kt;
12899fb66d81Schristos 	struct key_binding	*bd;
12909fb66d81Schristos 
12919fb66d81Schristos 	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
12929fb66d81Schristos 		return;
12939fb66d81Schristos 
12949fb66d81Schristos 	if (item == mode_tree_get_current(data->data)) {
12959fb66d81Schristos 		mode_tree_collapse_current(data->data);
12969fb66d81Schristos 		mode_tree_up(data->data, 0);
12979fb66d81Schristos 	}
12989fb66d81Schristos 	key_bindings_remove(kt->name, bd->key);
12999fb66d81Schristos }
13009fb66d81Schristos 
13019fb66d81Schristos static void
window_customize_reset_key(struct window_customize_modedata * data,struct window_customize_itemdata * item)13029fb66d81Schristos window_customize_reset_key(struct window_customize_modedata *data,
13039fb66d81Schristos     struct window_customize_itemdata *item)
13049fb66d81Schristos {
13059fb66d81Schristos 	struct key_table	*kt;
13069fb66d81Schristos 	struct key_binding	*dd, *bd;
13079fb66d81Schristos 
13089fb66d81Schristos 	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
13099fb66d81Schristos 		return;
13109fb66d81Schristos 
13119fb66d81Schristos 	dd = key_bindings_get_default(kt, bd->key);
13129fb66d81Schristos 	if (dd != NULL && bd->cmdlist == dd->cmdlist)
13139fb66d81Schristos 		return;
13149fb66d81Schristos 	if (dd == NULL && item == mode_tree_get_current(data->data)) {
13159fb66d81Schristos 		mode_tree_collapse_current(data->data);
13169fb66d81Schristos 		mode_tree_up(data->data, 0);
13179fb66d81Schristos 	}
13189fb66d81Schristos 	key_bindings_reset(kt->name, bd->key);
13199fb66d81Schristos }
13209fb66d81Schristos 
13219fb66d81Schristos static void
window_customize_change_each(void * modedata,void * itemdata,__unused struct client * c,__unused key_code key)13229fb66d81Schristos window_customize_change_each(void *modedata, void *itemdata,
13239fb66d81Schristos     __unused struct client *c, __unused key_code key)
13249fb66d81Schristos {
13259fb66d81Schristos 	struct window_customize_modedata	*data = modedata;
13269fb66d81Schristos 	struct window_customize_itemdata	*item = itemdata;
13279fb66d81Schristos 
13289fb66d81Schristos 	switch (data->change) {
13299fb66d81Schristos 	case WINDOW_CUSTOMIZE_UNSET:
13309fb66d81Schristos 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
13319fb66d81Schristos 			window_customize_unset_key(data, item);
13329fb66d81Schristos 		else
13339fb66d81Schristos 			window_customize_unset_option(data, item);
13349fb66d81Schristos 		break;
13359fb66d81Schristos 	case WINDOW_CUSTOMIZE_RESET:
13369fb66d81Schristos 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
13379fb66d81Schristos 			window_customize_reset_key(data, item);
13389fb66d81Schristos 		else
13399fb66d81Schristos 			window_customize_reset_option(data, item);
13409fb66d81Schristos 		break;
13419fb66d81Schristos 	}
13429fb66d81Schristos 	if (item->scope != WINDOW_CUSTOMIZE_KEY)
13439fb66d81Schristos 		options_push_changes(item->name);
13449fb66d81Schristos }
13459fb66d81Schristos 
13469fb66d81Schristos static int
window_customize_change_current_callback(__unused struct client * c,void * modedata,const char * s,__unused int done)13479fb66d81Schristos window_customize_change_current_callback(__unused struct client *c,
13489fb66d81Schristos     void *modedata, const char *s, __unused int done)
13499fb66d81Schristos {
13509fb66d81Schristos 	struct window_customize_modedata	*data = modedata;
13519fb66d81Schristos 	struct window_customize_itemdata	*item;
13529fb66d81Schristos 
13539fb66d81Schristos 	if (s == NULL || *s == '\0' || data->dead)
13549fb66d81Schristos 		return (0);
13559fb66d81Schristos 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
13569fb66d81Schristos 		return (0);
13579fb66d81Schristos 
13589fb66d81Schristos 	item = mode_tree_get_current(data->data);
13599fb66d81Schristos 	switch (data->change) {
13609fb66d81Schristos 	case WINDOW_CUSTOMIZE_UNSET:
13619fb66d81Schristos 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
13629fb66d81Schristos 			window_customize_unset_key(data, item);
13639fb66d81Schristos 		else
13649fb66d81Schristos 			window_customize_unset_option(data, item);
13659fb66d81Schristos 		break;
13669fb66d81Schristos 	case WINDOW_CUSTOMIZE_RESET:
13679fb66d81Schristos 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
13689fb66d81Schristos 			window_customize_reset_key(data, item);
13699fb66d81Schristos 		else
13709fb66d81Schristos 			window_customize_reset_option(data, item);
13719fb66d81Schristos 		break;
13729fb66d81Schristos 	}
13739fb66d81Schristos 	if (item->scope != WINDOW_CUSTOMIZE_KEY)
13749fb66d81Schristos 		options_push_changes(item->name);
13759fb66d81Schristos 	mode_tree_build(data->data);
13769fb66d81Schristos 	mode_tree_draw(data->data);
13779fb66d81Schristos 	data->wp->flags |= PANE_REDRAW;
13789fb66d81Schristos 
13799fb66d81Schristos 	return (0);
13809fb66d81Schristos }
13819fb66d81Schristos 
13829fb66d81Schristos static int
window_customize_change_tagged_callback(struct client * c,void * modedata,const char * s,__unused int done)13839fb66d81Schristos window_customize_change_tagged_callback(struct client *c, void *modedata,
13849fb66d81Schristos     const char *s, __unused int done)
13859fb66d81Schristos {
13869fb66d81Schristos 	struct window_customize_modedata	*data = modedata;
13879fb66d81Schristos 
13889fb66d81Schristos 	if (s == NULL || *s == '\0' || data->dead)
13899fb66d81Schristos 		return (0);
13909fb66d81Schristos 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
13919fb66d81Schristos 		return (0);
13929fb66d81Schristos 
13939fb66d81Schristos 	mode_tree_each_tagged(data->data, window_customize_change_each, c,
13949fb66d81Schristos 	    KEYC_NONE, 0);
13959fb66d81Schristos 	mode_tree_build(data->data);
13969fb66d81Schristos 	mode_tree_draw(data->data);
13979fb66d81Schristos 	data->wp->flags |= PANE_REDRAW;
13989fb66d81Schristos 
13999fb66d81Schristos 	return (0);
14009fb66d81Schristos }
14019fb66d81Schristos 
14029fb66d81Schristos static void
window_customize_key(struct window_mode_entry * wme,struct client * c,__unused struct session * s,__unused struct winlink * wl,key_code key,struct mouse_event * m)14039fb66d81Schristos window_customize_key(struct window_mode_entry *wme, struct client *c,
14049fb66d81Schristos     __unused struct session *s, __unused struct winlink *wl, key_code key,
14059fb66d81Schristos     struct mouse_event *m)
14069fb66d81Schristos {
14079fb66d81Schristos 	struct window_pane			*wp = wme->wp;
14089fb66d81Schristos 	struct window_customize_modedata	*data = wme->data;
14099fb66d81Schristos 	struct window_customize_itemdata	*item, *new_item;
14109fb66d81Schristos 	int					 finished, idx;
14119fb66d81Schristos 	char					*prompt;
14129fb66d81Schristos 	u_int					 tagged;
14139fb66d81Schristos 
14149fb66d81Schristos 	item = mode_tree_get_current(data->data);
14159fb66d81Schristos 	finished = mode_tree_key(data->data, c, &key, m, NULL, NULL);
14169fb66d81Schristos 	if (item != (new_item = mode_tree_get_current(data->data)))
14179fb66d81Schristos 		item = new_item;
14189fb66d81Schristos 
14199fb66d81Schristos 	switch (key) {
14209fb66d81Schristos 	case '\r':
14219fb66d81Schristos 	case 's':
14229fb66d81Schristos 		if (item == NULL)
14239fb66d81Schristos 			break;
14249fb66d81Schristos 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
14259fb66d81Schristos 			window_customize_set_key(c, data, item);
14269fb66d81Schristos 		else {
14279fb66d81Schristos 			window_customize_set_option(c, data, item, 0, 1);
14289fb66d81Schristos 			options_push_changes(item->name);
14299fb66d81Schristos 		}
14309fb66d81Schristos 		mode_tree_build(data->data);
14319fb66d81Schristos 		break;
14329fb66d81Schristos 	case 'w':
14339fb66d81Schristos 		if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY)
14349fb66d81Schristos 			break;
14359fb66d81Schristos 		window_customize_set_option(c, data, item, 0, 0);
14369fb66d81Schristos 		options_push_changes(item->name);
14379fb66d81Schristos 		mode_tree_build(data->data);
14389fb66d81Schristos 		break;
14399fb66d81Schristos 	case 'S':
14409fb66d81Schristos 	case 'W':
14419fb66d81Schristos 		if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY)
14429fb66d81Schristos 			break;
14439fb66d81Schristos 		window_customize_set_option(c, data, item, 1, 0);
14449fb66d81Schristos 		options_push_changes(item->name);
14459fb66d81Schristos 		mode_tree_build(data->data);
14469fb66d81Schristos 		break;
14479fb66d81Schristos 	case 'd':
14489fb66d81Schristos 		if (item == NULL || item->idx != -1)
14499fb66d81Schristos 			break;
14509fb66d81Schristos 		xasprintf(&prompt, "Reset %s to default? ", item->name);
14519fb66d81Schristos 		data->references++;
14529fb66d81Schristos 		data->change = WINDOW_CUSTOMIZE_RESET;
14539fb66d81Schristos 		status_prompt_set(c, NULL, prompt, "",
14549fb66d81Schristos 		    window_customize_change_current_callback,
14559fb66d81Schristos 		    window_customize_free_callback, data,
1456*46548964Swiz 		    PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
14579fb66d81Schristos 		free(prompt);
14589fb66d81Schristos 		break;
14599fb66d81Schristos 	case 'D':
14609fb66d81Schristos 		tagged = mode_tree_count_tagged(data->data);
14619fb66d81Schristos 		if (tagged == 0)
14629fb66d81Schristos 			break;
14639fb66d81Schristos 		xasprintf(&prompt, "Reset %u tagged to default? ", tagged);
14649fb66d81Schristos 		data->references++;
14659fb66d81Schristos 		data->change = WINDOW_CUSTOMIZE_RESET;
14669fb66d81Schristos 		status_prompt_set(c, NULL, prompt, "",
14679fb66d81Schristos 		    window_customize_change_tagged_callback,
14689fb66d81Schristos 		    window_customize_free_callback, data,
1469*46548964Swiz 		    PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
14709fb66d81Schristos 		free(prompt);
14719fb66d81Schristos 		break;
14729fb66d81Schristos 	case 'u':
14739fb66d81Schristos 		if (item == NULL)
14749fb66d81Schristos 			break;
14759fb66d81Schristos 		idx = item->idx;
14769fb66d81Schristos 		if (idx != -1)
14779fb66d81Schristos 			xasprintf(&prompt, "Unset %s[%d]? ", item->name, idx);
14789fb66d81Schristos 		else
14799fb66d81Schristos 			xasprintf(&prompt, "Unset %s? ", item->name);
14809fb66d81Schristos 		data->references++;
14819fb66d81Schristos 		data->change = WINDOW_CUSTOMIZE_UNSET;
14829fb66d81Schristos 		status_prompt_set(c, NULL, prompt, "",
14839fb66d81Schristos 		    window_customize_change_current_callback,
14849fb66d81Schristos 		    window_customize_free_callback, data,
1485*46548964Swiz 		    PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
14869fb66d81Schristos 		free(prompt);
14879fb66d81Schristos 		break;
14889fb66d81Schristos 	case 'U':
14899fb66d81Schristos 		tagged = mode_tree_count_tagged(data->data);
14909fb66d81Schristos 		if (tagged == 0)
14919fb66d81Schristos 			break;
14929fb66d81Schristos 		xasprintf(&prompt, "Unset %u tagged? ", tagged);
14939fb66d81Schristos 		data->references++;
14949fb66d81Schristos 		data->change = WINDOW_CUSTOMIZE_UNSET;
14959fb66d81Schristos 		status_prompt_set(c, NULL, prompt, "",
14969fb66d81Schristos 		    window_customize_change_tagged_callback,
14979fb66d81Schristos 		    window_customize_free_callback, data,
1498*46548964Swiz 		    PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
14999fb66d81Schristos 		free(prompt);
15009fb66d81Schristos 		break;
15019fb66d81Schristos 	case 'H':
15029fb66d81Schristos 		data->hide_global = !data->hide_global;
15039fb66d81Schristos 		mode_tree_build(data->data);
15049fb66d81Schristos 		break;
15059fb66d81Schristos 	}
15069fb66d81Schristos 	if (finished)
15079fb66d81Schristos 		window_pane_reset_mode(wp);
15089fb66d81Schristos 	else {
15099fb66d81Schristos 		mode_tree_draw(data->data);
15109fb66d81Schristos 		wp->flags |= PANE_REDRAW;
15119fb66d81Schristos 	}
15129fb66d81Schristos }
1513