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