1 /* $OpenBSD: window-customize.c,v 1.13 2021/11/29 11:01:51 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <ctype.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include "tmux.h" 26 27 static struct screen *window_customize_init(struct window_mode_entry *, 28 struct cmd_find_state *, struct args *); 29 static void window_customize_free(struct window_mode_entry *); 30 static void window_customize_resize(struct window_mode_entry *, 31 u_int, u_int); 32 static void window_customize_key(struct window_mode_entry *, 33 struct client *, struct session *, 34 struct winlink *, key_code, struct mouse_event *); 35 36 #define WINDOW_CUSTOMIZE_DEFAULT_FORMAT \ 37 "#{?is_option," \ 38 "#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \ 39 "#[ignore]" \ 40 "#{option_value}#{?option_unit, #{option_unit},}" \ 41 "," \ 42 "#{key}" \ 43 "}" 44 45 static const struct menu_item window_customize_menu_items[] = { 46 { "Select", '\r', NULL }, 47 { "Expand", KEYC_RIGHT, NULL }, 48 { "", KEYC_NONE, NULL }, 49 { "Tag", 't', NULL }, 50 { "Tag All", '\024', NULL }, 51 { "Tag None", 'T', NULL }, 52 { "", KEYC_NONE, NULL }, 53 { "Cancel", 'q', NULL }, 54 55 { NULL, KEYC_NONE, NULL } 56 }; 57 58 const struct window_mode window_customize_mode = { 59 .name = "options-mode", 60 .default_format = WINDOW_CUSTOMIZE_DEFAULT_FORMAT, 61 62 .init = window_customize_init, 63 .free = window_customize_free, 64 .resize = window_customize_resize, 65 .key = window_customize_key, 66 }; 67 68 enum window_customize_scope { 69 WINDOW_CUSTOMIZE_NONE, 70 WINDOW_CUSTOMIZE_KEY, 71 WINDOW_CUSTOMIZE_SERVER, 72 WINDOW_CUSTOMIZE_GLOBAL_SESSION, 73 WINDOW_CUSTOMIZE_SESSION, 74 WINDOW_CUSTOMIZE_GLOBAL_WINDOW, 75 WINDOW_CUSTOMIZE_WINDOW, 76 WINDOW_CUSTOMIZE_PANE 77 }; 78 79 enum window_customize_change { 80 WINDOW_CUSTOMIZE_UNSET, 81 WINDOW_CUSTOMIZE_RESET, 82 }; 83 84 struct window_customize_itemdata { 85 struct window_customize_modedata *data; 86 enum window_customize_scope scope; 87 88 char *table; 89 key_code key; 90 91 struct options *oo; 92 char *name; 93 int idx; 94 }; 95 96 struct window_customize_modedata { 97 struct window_pane *wp; 98 int dead; 99 int references; 100 101 struct mode_tree_data *data; 102 char *format; 103 int hide_global; 104 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 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 ((uint64_t)o); 120 offset = ((char *)oe - (char *)options_table) / sizeof *options_table; 121 return ((2ULL << 62)|(offset << 32)|((idx + 1) << 1)|1); 122 } 123 124 static struct options * 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 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 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 * 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 * 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 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 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 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 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 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 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, (uint64_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 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 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 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) 684 text = "This is a user option."; 685 else if (oe->text == NULL) 686 text = "This option doesn't have a description."; 687 else 688 text = oe->text; 689 if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s", 690 text)) 691 goto out; 692 screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ 693 if (s->cy >= cy + sy - 1) 694 goto out; 695 696 if (oe == NULL) 697 text = "user"; 698 else if ((oe->scope & (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) == 699 (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) 700 text = "window and pane"; 701 else if (oe->scope & OPTIONS_TABLE_WINDOW) 702 text = "window"; 703 else if (oe->scope & OPTIONS_TABLE_SESSION) 704 text = "session"; 705 else 706 text = "server"; 707 if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, 708 &grid_default_cell, "This is a %s option.", text)) 709 goto out; 710 if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { 711 if (idx != -1) { 712 if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 713 0, &grid_default_cell, 714 "This is an array option, index %u.", idx)) 715 goto out; 716 } else { 717 if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 718 0, &grid_default_cell, "This is an array option.")) 719 goto out; 720 } 721 if (idx == -1) 722 goto out; 723 } 724 screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ 725 if (s->cy >= cy + sy - 1) 726 goto out; 727 728 value = options_to_string(o, idx, 0); 729 if (oe != NULL && idx == -1) { 730 default_value = options_default_to_string(oe); 731 if (strcmp(default_value, value) == 0) { 732 free(default_value); 733 default_value = NULL; 734 } 735 } 736 if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, 737 &grid_default_cell, "Option value: %s%s%s", value, space, unit)) 738 goto out; 739 if (oe == NULL || oe->type == OPTIONS_TABLE_STRING) { 740 expanded = format_expand(ft, value); 741 if (strcmp(expanded, value) != 0) { 742 if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 743 0, &grid_default_cell, "This expands to: %s", 744 expanded)) 745 goto out; 746 } 747 free(expanded); 748 } 749 if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) { 750 for (choice = oe->choices; *choice != NULL; choice++) { 751 strlcat(choices, *choice, sizeof choices); 752 strlcat(choices, ", ", sizeof choices); 753 } 754 choices[strlen(choices) - 2] = '\0'; 755 if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, 756 &grid_default_cell, "Available values are: %s", 757 choices)) 758 goto out; 759 } 760 if (oe != NULL && oe->type == OPTIONS_TABLE_COLOUR) { 761 if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1, 762 &grid_default_cell, "This is a colour option: ")) 763 goto out; 764 memcpy(&gc, &grid_default_cell, sizeof gc); 765 gc.fg = options_get_number(item->oo, name); 766 if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc, 767 "EXAMPLE")) 768 goto out; 769 } 770 if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_STYLE)) { 771 if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1, 772 &grid_default_cell, "This is a style option: ")) 773 goto out; 774 style_apply(&gc, item->oo, name, ft); 775 if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc, 776 "EXAMPLE")) 777 goto out; 778 } 779 if (default_value != NULL) { 780 if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, 781 &grid_default_cell, "The default is: %s%s%s", default_value, 782 space, unit)) 783 goto out; 784 } 785 786 screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ 787 if (s->cy > cy + sy - 1) 788 goto out; 789 if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { 790 wo = NULL; 791 go = NULL; 792 } else { 793 switch (item->scope) { 794 case WINDOW_CUSTOMIZE_PANE: 795 wo = options_get_parent(item->oo); 796 go = options_get_parent(wo); 797 break; 798 case WINDOW_CUSTOMIZE_WINDOW: 799 case WINDOW_CUSTOMIZE_SESSION: 800 wo = NULL; 801 go = options_get_parent(item->oo); 802 break; 803 default: 804 wo = NULL; 805 go = NULL; 806 break; 807 } 808 } 809 if (wo != NULL && options_owner(o) != wo) { 810 parent = options_get_only(wo, name); 811 if (parent != NULL) { 812 value = options_to_string(parent, -1 , 0); 813 if (!screen_write_text(ctx, s->cx, sx, 814 sy - (s->cy - cy), 0, &grid_default_cell, 815 "Window value (from window %u): %s%s%s", fs.wl->idx, 816 value, space, unit)) 817 goto out; 818 } 819 } 820 if (go != NULL && options_owner(o) != go) { 821 parent = options_get_only(go, name); 822 if (parent != NULL) { 823 value = options_to_string(parent, -1 , 0); 824 if (!screen_write_text(ctx, s->cx, sx, 825 sy - (s->cy - cy), 0, &grid_default_cell, 826 "Global value: %s%s%s", value, space, unit)) 827 goto out; 828 } 829 } 830 831 out: 832 free(value); 833 free(default_value); 834 format_free(ft); 835 } 836 837 static void 838 window_customize_draw(void *modedata, void *itemdata, 839 struct screen_write_ctx *ctx, u_int sx, u_int sy) 840 { 841 struct window_customize_modedata *data = modedata; 842 struct window_customize_itemdata *item = itemdata; 843 844 if (item == NULL) 845 return; 846 847 if (item->scope == WINDOW_CUSTOMIZE_KEY) 848 window_customize_draw_key(data, item, ctx, sx, sy); 849 else 850 window_customize_draw_option(data, item, ctx, sx, sy); 851 } 852 853 static void 854 window_customize_menu(void *modedata, struct client *c, key_code key) 855 { 856 struct window_customize_modedata *data = modedata; 857 struct window_pane *wp = data->wp; 858 struct window_mode_entry *wme; 859 860 wme = TAILQ_FIRST(&wp->modes); 861 if (wme == NULL || wme->data != modedata) 862 return; 863 window_customize_key(wme, c, NULL, NULL, key, NULL); 864 } 865 866 static u_int 867 window_customize_height(__unused void *modedata, __unused u_int height) 868 { 869 return (12); 870 } 871 872 static struct screen * 873 window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs, 874 struct args *args) 875 { 876 struct window_pane *wp = wme->wp; 877 struct window_customize_modedata *data; 878 struct screen *s; 879 880 wme->data = data = xcalloc(1, sizeof *data); 881 data->wp = wp; 882 data->references = 1; 883 884 memcpy(&data->fs, fs, sizeof data->fs); 885 886 if (args == NULL || !args_has(args, 'F')) 887 data->format = xstrdup(WINDOW_CUSTOMIZE_DEFAULT_FORMAT); 888 else 889 data->format = xstrdup(args_get(args, 'F')); 890 891 data->data = mode_tree_start(wp, args, window_customize_build, 892 window_customize_draw, NULL, window_customize_menu, 893 window_customize_height, NULL, data, window_customize_menu_items, 894 NULL, 0, &s); 895 mode_tree_zoom(data->data, args); 896 897 mode_tree_build(data->data); 898 mode_tree_draw(data->data); 899 900 return (s); 901 } 902 903 static void 904 window_customize_destroy(struct window_customize_modedata *data) 905 { 906 u_int i; 907 908 if (--data->references != 0) 909 return; 910 911 for (i = 0; i < data->item_size; i++) 912 window_customize_free_item(data->item_list[i]); 913 free(data->item_list); 914 915 free(data->format); 916 917 free(data); 918 } 919 920 static void 921 window_customize_free(struct window_mode_entry *wme) 922 { 923 struct window_customize_modedata *data = wme->data; 924 925 if (data == NULL) 926 return; 927 928 data->dead = 1; 929 mode_tree_free(data->data); 930 window_customize_destroy(data); 931 } 932 933 static void 934 window_customize_resize(struct window_mode_entry *wme, u_int sx, u_int sy) 935 { 936 struct window_customize_modedata *data = wme->data; 937 938 mode_tree_resize(data->data, sx, sy); 939 } 940 941 static void 942 window_customize_free_callback(void *modedata) 943 { 944 window_customize_destroy(modedata); 945 } 946 947 static void 948 window_customize_free_item_callback(void *itemdata) 949 { 950 struct window_customize_itemdata *item = itemdata; 951 struct window_customize_modedata *data = item->data; 952 953 window_customize_free_item(item); 954 window_customize_destroy(data); 955 } 956 957 static int 958 window_customize_set_option_callback(struct client *c, void *itemdata, 959 const char *s, __unused int done) 960 { 961 struct window_customize_itemdata *item = itemdata; 962 struct window_customize_modedata *data = item->data; 963 struct options_entry *o; 964 const struct options_table_entry *oe; 965 struct options *oo = item->oo; 966 const char *name = item->name; 967 char *cause; 968 int idx = item->idx; 969 970 if (s == NULL || *s == '\0' || data->dead) 971 return (0); 972 if (item == NULL || !window_customize_check_item(data, item, NULL)) 973 return (0); 974 o = options_get(oo, name); 975 if (o == NULL) 976 return (0); 977 oe = options_table_entry(o); 978 979 if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { 980 if (idx == -1) { 981 for (idx = 0; idx < INT_MAX; idx++) { 982 if (options_array_get(o, idx) == NULL) 983 break; 984 } 985 } 986 if (options_array_set(o, idx, s, 0, &cause) != 0) 987 goto fail; 988 } else { 989 if (options_from_string(oo, oe, name, s, 0, &cause) != 0) 990 goto fail; 991 } 992 993 options_push_changes(item->name); 994 mode_tree_build(data->data); 995 mode_tree_draw(data->data); 996 data->wp->flags |= PANE_REDRAW; 997 998 return (0); 999 1000 fail: 1001 *cause = toupper((u_char)*cause); 1002 status_message_set(c, -1, 1, 0, "%s", cause); 1003 free(cause); 1004 return (0); 1005 } 1006 1007 static void 1008 window_customize_set_option(struct client *c, 1009 struct window_customize_modedata *data, 1010 struct window_customize_itemdata *item, int global, int pane) 1011 { 1012 struct options_entry *o; 1013 const struct options_table_entry *oe; 1014 struct options *oo; 1015 struct window_customize_itemdata *new_item; 1016 int flag, idx = item->idx; 1017 enum window_customize_scope scope = WINDOW_CUSTOMIZE_NONE; 1018 u_int choice; 1019 const char *name = item->name, *space = ""; 1020 char *prompt, *value, *text; 1021 struct cmd_find_state fs; 1022 1023 if (item == NULL || !window_customize_check_item(data, item, &fs)) 1024 return; 1025 o = options_get(item->oo, name); 1026 if (o == NULL) 1027 return; 1028 1029 oe = options_table_entry(o); 1030 if (oe != NULL && ~oe->scope & OPTIONS_TABLE_PANE) 1031 pane = 0; 1032 if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { 1033 scope = item->scope; 1034 oo = item->oo; 1035 } else { 1036 if (global) { 1037 switch (item->scope) { 1038 case WINDOW_CUSTOMIZE_NONE: 1039 case WINDOW_CUSTOMIZE_KEY: 1040 case WINDOW_CUSTOMIZE_SERVER: 1041 case WINDOW_CUSTOMIZE_GLOBAL_SESSION: 1042 case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: 1043 scope = item->scope; 1044 break; 1045 case WINDOW_CUSTOMIZE_SESSION: 1046 scope = WINDOW_CUSTOMIZE_GLOBAL_SESSION; 1047 break; 1048 case WINDOW_CUSTOMIZE_WINDOW: 1049 case WINDOW_CUSTOMIZE_PANE: 1050 scope = WINDOW_CUSTOMIZE_GLOBAL_WINDOW; 1051 break; 1052 } 1053 } else { 1054 switch (item->scope) { 1055 case WINDOW_CUSTOMIZE_NONE: 1056 case WINDOW_CUSTOMIZE_KEY: 1057 case WINDOW_CUSTOMIZE_SERVER: 1058 case WINDOW_CUSTOMIZE_SESSION: 1059 scope = item->scope; 1060 break; 1061 case WINDOW_CUSTOMIZE_WINDOW: 1062 case WINDOW_CUSTOMIZE_PANE: 1063 if (pane) 1064 scope = WINDOW_CUSTOMIZE_PANE; 1065 else 1066 scope = WINDOW_CUSTOMIZE_WINDOW; 1067 break; 1068 case WINDOW_CUSTOMIZE_GLOBAL_SESSION: 1069 scope = WINDOW_CUSTOMIZE_SESSION; 1070 break; 1071 case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: 1072 if (pane) 1073 scope = WINDOW_CUSTOMIZE_PANE; 1074 else 1075 scope = WINDOW_CUSTOMIZE_WINDOW; 1076 break; 1077 } 1078 } 1079 if (scope == item->scope) 1080 oo = item->oo; 1081 else 1082 oo = window_customize_get_tree(scope, &fs); 1083 } 1084 1085 if (oe != NULL && oe->type == OPTIONS_TABLE_FLAG) { 1086 flag = options_get_number(oo, name); 1087 options_set_number(oo, name, !flag); 1088 } else if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) { 1089 choice = options_get_number(oo, name); 1090 if (oe->choices[choice + 1] == NULL) 1091 choice = 0; 1092 else 1093 choice++; 1094 options_set_number(oo, name, choice); 1095 } else { 1096 text = window_customize_scope_text(scope, &fs); 1097 if (*text != '\0') 1098 space = ", for "; 1099 else if (scope != WINDOW_CUSTOMIZE_SERVER) 1100 space = ", global"; 1101 if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { 1102 if (idx == -1) { 1103 xasprintf(&prompt, "(%s[+]%s%s) ", name, space, 1104 text); 1105 } else { 1106 xasprintf(&prompt, "(%s[%d]%s%s) ", name, idx, 1107 space, text); 1108 } 1109 } else 1110 xasprintf(&prompt, "(%s%s%s) ", name, space, text); 1111 free(text); 1112 1113 value = options_to_string(o, idx, 0); 1114 1115 new_item = xcalloc(1, sizeof *new_item); 1116 new_item->data = data; 1117 new_item->scope = scope; 1118 new_item->oo = oo; 1119 new_item->name = xstrdup(name); 1120 new_item->idx = idx; 1121 1122 data->references++; 1123 status_prompt_set(c, NULL, prompt, value, 1124 window_customize_set_option_callback, 1125 window_customize_free_item_callback, new_item, 1126 PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); 1127 1128 free(prompt); 1129 free(value); 1130 } 1131 } 1132 1133 static void 1134 window_customize_unset_option(struct window_customize_modedata *data, 1135 struct window_customize_itemdata *item) 1136 { 1137 struct options_entry *o; 1138 1139 if (item == NULL || !window_customize_check_item(data, item, NULL)) 1140 return; 1141 1142 o = options_get(item->oo, item->name); 1143 if (o == NULL) 1144 return; 1145 if (item->idx != -1 && item == mode_tree_get_current(data->data)) 1146 mode_tree_up(data->data, 0); 1147 options_remove_or_default(o, item->idx, NULL); 1148 } 1149 1150 static void 1151 window_customize_reset_option(struct window_customize_modedata *data, 1152 struct window_customize_itemdata *item) 1153 { 1154 struct options *oo; 1155 struct options_entry *o; 1156 1157 if (item == NULL || !window_customize_check_item(data, item, NULL)) 1158 return; 1159 if (item->idx != -1) 1160 return; 1161 1162 oo = item->oo; 1163 while (oo != NULL) { 1164 o = options_get_only(item->oo, item->name); 1165 if (o != NULL) 1166 options_remove_or_default(o, -1, NULL); 1167 oo = options_get_parent(oo); 1168 } 1169 } 1170 1171 static int 1172 window_customize_set_command_callback(struct client *c, void *itemdata, 1173 const char *s, __unused int done) 1174 { 1175 struct window_customize_itemdata *item = itemdata; 1176 struct window_customize_modedata *data = item->data; 1177 struct key_binding *bd; 1178 struct cmd_parse_result *pr; 1179 char *error; 1180 1181 if (s == NULL || *s == '\0' || data->dead) 1182 return (0); 1183 if (item == NULL || !window_customize_get_key(item, NULL, &bd)) 1184 return (0); 1185 1186 pr = cmd_parse_from_string(s, NULL); 1187 switch (pr->status) { 1188 case CMD_PARSE_ERROR: 1189 error = pr->error; 1190 goto fail; 1191 case CMD_PARSE_SUCCESS: 1192 break; 1193 } 1194 cmd_list_free(bd->cmdlist); 1195 bd->cmdlist = pr->cmdlist; 1196 1197 mode_tree_build(data->data); 1198 mode_tree_draw(data->data); 1199 data->wp->flags |= PANE_REDRAW; 1200 1201 return (0); 1202 1203 fail: 1204 *error = toupper((u_char)*error); 1205 status_message_set(c, -1, 1, 0, "%s", error); 1206 free(error); 1207 return (0); 1208 } 1209 1210 static int 1211 window_customize_set_note_callback(__unused struct client *c, void *itemdata, 1212 const char *s, __unused int done) 1213 { 1214 struct window_customize_itemdata *item = itemdata; 1215 struct window_customize_modedata *data = item->data; 1216 struct key_binding *bd; 1217 1218 if (s == NULL || *s == '\0' || data->dead) 1219 return (0); 1220 if (item == NULL || !window_customize_get_key(item, NULL, &bd)) 1221 return (0); 1222 1223 free((void *)bd->note); 1224 bd->note = xstrdup(s); 1225 1226 mode_tree_build(data->data); 1227 mode_tree_draw(data->data); 1228 data->wp->flags |= PANE_REDRAW; 1229 1230 return (0); 1231 } 1232 1233 static void 1234 window_customize_set_key(struct client *c, 1235 struct window_customize_modedata *data, 1236 struct window_customize_itemdata *item) 1237 { 1238 key_code key = item->key; 1239 struct key_binding *bd; 1240 const char *s; 1241 char *prompt, *value; 1242 struct window_customize_itemdata *new_item; 1243 1244 if (item == NULL || !window_customize_get_key(item, NULL, &bd)) 1245 return; 1246 1247 s = mode_tree_get_current_name(data->data); 1248 if (strcmp(s, "Repeat") == 0) 1249 bd->flags ^= KEY_BINDING_REPEAT; 1250 else if (strcmp(s, "Command") == 0) { 1251 xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0)); 1252 value = cmd_list_print(bd->cmdlist, 0); 1253 1254 new_item = xcalloc(1, sizeof *new_item); 1255 new_item->data = data; 1256 new_item->scope = item->scope; 1257 new_item->table = xstrdup(item->table); 1258 new_item->key = key; 1259 1260 data->references++; 1261 status_prompt_set(c, NULL, prompt, value, 1262 window_customize_set_command_callback, 1263 window_customize_free_item_callback, new_item, 1264 PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); 1265 free(prompt); 1266 free(value); 1267 } else if (strcmp(s, "Note") == 0) { 1268 xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0)); 1269 1270 new_item = xcalloc(1, sizeof *new_item); 1271 new_item->data = data; 1272 new_item->scope = item->scope; 1273 new_item->table = xstrdup(item->table); 1274 new_item->key = key; 1275 1276 data->references++; 1277 status_prompt_set(c, NULL, prompt, 1278 (bd->note == NULL ? "" : bd->note), 1279 window_customize_set_note_callback, 1280 window_customize_free_item_callback, new_item, 1281 PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); 1282 free(prompt); 1283 } 1284 } 1285 1286 static void 1287 window_customize_unset_key(struct window_customize_modedata *data, 1288 struct window_customize_itemdata *item) 1289 { 1290 struct key_table *kt; 1291 struct key_binding *bd; 1292 1293 if (item == NULL || !window_customize_get_key(item, &kt, &bd)) 1294 return; 1295 1296 if (item == mode_tree_get_current(data->data)) { 1297 mode_tree_collapse_current(data->data); 1298 mode_tree_up(data->data, 0); 1299 } 1300 key_bindings_remove(kt->name, bd->key); 1301 } 1302 1303 static void 1304 window_customize_reset_key(struct window_customize_modedata *data, 1305 struct window_customize_itemdata *item) 1306 { 1307 struct key_table *kt; 1308 struct key_binding *dd, *bd; 1309 1310 if (item == NULL || !window_customize_get_key(item, &kt, &bd)) 1311 return; 1312 1313 dd = key_bindings_get_default(kt, bd->key); 1314 if (dd != NULL && bd->cmdlist == dd->cmdlist) 1315 return; 1316 if (dd == NULL && item == mode_tree_get_current(data->data)) { 1317 mode_tree_collapse_current(data->data); 1318 mode_tree_up(data->data, 0); 1319 } 1320 key_bindings_reset(kt->name, bd->key); 1321 } 1322 1323 static void 1324 window_customize_change_each(void *modedata, void *itemdata, 1325 __unused struct client *c, __unused key_code key) 1326 { 1327 struct window_customize_modedata *data = modedata; 1328 struct window_customize_itemdata *item = itemdata; 1329 1330 switch (data->change) { 1331 case WINDOW_CUSTOMIZE_UNSET: 1332 if (item->scope == WINDOW_CUSTOMIZE_KEY) 1333 window_customize_unset_key(data, item); 1334 else 1335 window_customize_unset_option(data, item); 1336 break; 1337 case WINDOW_CUSTOMIZE_RESET: 1338 if (item->scope == WINDOW_CUSTOMIZE_KEY) 1339 window_customize_reset_key(data, item); 1340 else 1341 window_customize_reset_option(data, item); 1342 break; 1343 } 1344 if (item->scope != WINDOW_CUSTOMIZE_KEY) 1345 options_push_changes(item->name); 1346 } 1347 1348 static int 1349 window_customize_change_current_callback(__unused struct client *c, 1350 void *modedata, const char *s, __unused int done) 1351 { 1352 struct window_customize_modedata *data = modedata; 1353 struct window_customize_itemdata *item; 1354 1355 if (s == NULL || *s == '\0' || data->dead) 1356 return (0); 1357 if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1358 return (0); 1359 1360 item = mode_tree_get_current(data->data); 1361 switch (data->change) { 1362 case WINDOW_CUSTOMIZE_UNSET: 1363 if (item->scope == WINDOW_CUSTOMIZE_KEY) 1364 window_customize_unset_key(data, item); 1365 else 1366 window_customize_unset_option(data, item); 1367 break; 1368 case WINDOW_CUSTOMIZE_RESET: 1369 if (item->scope == WINDOW_CUSTOMIZE_KEY) 1370 window_customize_reset_key(data, item); 1371 else 1372 window_customize_reset_option(data, item); 1373 break; 1374 } 1375 if (item->scope != WINDOW_CUSTOMIZE_KEY) 1376 options_push_changes(item->name); 1377 mode_tree_build(data->data); 1378 mode_tree_draw(data->data); 1379 data->wp->flags |= PANE_REDRAW; 1380 1381 return (0); 1382 } 1383 1384 static int 1385 window_customize_change_tagged_callback(struct client *c, void *modedata, 1386 const char *s, __unused int done) 1387 { 1388 struct window_customize_modedata *data = modedata; 1389 1390 if (s == NULL || *s == '\0' || data->dead) 1391 return (0); 1392 if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1393 return (0); 1394 1395 mode_tree_each_tagged(data->data, window_customize_change_each, c, 1396 KEYC_NONE, 0); 1397 mode_tree_build(data->data); 1398 mode_tree_draw(data->data); 1399 data->wp->flags |= PANE_REDRAW; 1400 1401 return (0); 1402 } 1403 1404 static void 1405 window_customize_key(struct window_mode_entry *wme, struct client *c, 1406 __unused struct session *s, __unused struct winlink *wl, key_code key, 1407 struct mouse_event *m) 1408 { 1409 struct window_pane *wp = wme->wp; 1410 struct window_customize_modedata *data = wme->data; 1411 struct window_customize_itemdata *item, *new_item; 1412 int finished, idx; 1413 char *prompt; 1414 u_int tagged; 1415 1416 item = mode_tree_get_current(data->data); 1417 finished = mode_tree_key(data->data, c, &key, m, NULL, NULL); 1418 if (item != (new_item = mode_tree_get_current(data->data))) 1419 item = new_item; 1420 1421 switch (key) { 1422 case '\r': 1423 case 's': 1424 if (item == NULL) 1425 break; 1426 if (item->scope == WINDOW_CUSTOMIZE_KEY) 1427 window_customize_set_key(c, data, item); 1428 else { 1429 window_customize_set_option(c, data, item, 0, 1); 1430 options_push_changes(item->name); 1431 } 1432 mode_tree_build(data->data); 1433 break; 1434 case 'w': 1435 if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY) 1436 break; 1437 window_customize_set_option(c, data, item, 0, 0); 1438 options_push_changes(item->name); 1439 mode_tree_build(data->data); 1440 break; 1441 case 'S': 1442 case 'W': 1443 if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY) 1444 break; 1445 window_customize_set_option(c, data, item, 1, 0); 1446 options_push_changes(item->name); 1447 mode_tree_build(data->data); 1448 break; 1449 case 'd': 1450 if (item == NULL || item->idx != -1) 1451 break; 1452 xasprintf(&prompt, "Reset %s to default? ", item->name); 1453 data->references++; 1454 data->change = WINDOW_CUSTOMIZE_RESET; 1455 status_prompt_set(c, NULL, prompt, "", 1456 window_customize_change_current_callback, 1457 window_customize_free_callback, data, 1458 PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); 1459 free(prompt); 1460 break; 1461 case 'D': 1462 tagged = mode_tree_count_tagged(data->data); 1463 if (tagged == 0) 1464 break; 1465 xasprintf(&prompt, "Reset %u tagged to default? ", tagged); 1466 data->references++; 1467 data->change = WINDOW_CUSTOMIZE_RESET; 1468 status_prompt_set(c, NULL, prompt, "", 1469 window_customize_change_tagged_callback, 1470 window_customize_free_callback, data, 1471 PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); 1472 free(prompt); 1473 break; 1474 case 'u': 1475 if (item == NULL) 1476 break; 1477 idx = item->idx; 1478 if (idx != -1) 1479 xasprintf(&prompt, "Unset %s[%d]? ", item->name, idx); 1480 else 1481 xasprintf(&prompt, "Unset %s? ", item->name); 1482 data->references++; 1483 data->change = WINDOW_CUSTOMIZE_UNSET; 1484 status_prompt_set(c, NULL, prompt, "", 1485 window_customize_change_current_callback, 1486 window_customize_free_callback, data, 1487 PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); 1488 free(prompt); 1489 break; 1490 case 'U': 1491 tagged = mode_tree_count_tagged(data->data); 1492 if (tagged == 0) 1493 break; 1494 xasprintf(&prompt, "Unset %u tagged? ", tagged); 1495 data->references++; 1496 data->change = WINDOW_CUSTOMIZE_UNSET; 1497 status_prompt_set(c, NULL, prompt, "", 1498 window_customize_change_tagged_callback, 1499 window_customize_free_callback, data, 1500 PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); 1501 free(prompt); 1502 break; 1503 case 'H': 1504 data->hide_global = !data->hide_global; 1505 mode_tree_build(data->data); 1506 break; 1507 } 1508 if (finished) 1509 window_pane_reset_mode(wp); 1510 else { 1511 mode_tree_draw(data->data); 1512 wp->flags |= PANE_REDRAW; 1513 } 1514 } 1515