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