Coverage Report

Created: 2025-11-19 06:37

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