| /* $OpenBSD$ */ |
| |
| /* |
| * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com> |
| * |
| * Permission to use, copy, modify, and distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
| * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include <sys/types.h> |
| |
| #include <ctype.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "tmux.h" |
| |
| static struct screen *window_customize_init(struct window_mode_entry *, |
| struct cmd_find_state *, struct args *); |
| static void window_customize_free(struct window_mode_entry *); |
| static void window_customize_resize(struct window_mode_entry *, |
| u_int, u_int); |
| static void window_customize_key(struct window_mode_entry *, |
| struct client *, struct session *, |
| struct winlink *, key_code, struct mouse_event *); |
| |
| #define WINDOW_CUSTOMIZE_DEFAULT_FORMAT \ |
| "#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \ |
| "#[ignore]" \ |
| "#{option_value}#{?option_unit, #{option_unit},}" |
| |
| static const struct menu_item window_customize_menu_items[] = { |
| { "Select", '\r', NULL }, |
| { "Expand", KEYC_RIGHT, NULL }, |
| { "", KEYC_NONE, NULL }, |
| { "Tag", 't', NULL }, |
| { "Tag All", '\024', NULL }, |
| { "Tag None", 'T', NULL }, |
| { "", KEYC_NONE, NULL }, |
| { "Cancel", 'q', NULL }, |
| |
| { NULL, KEYC_NONE, NULL } |
| }; |
| |
| const struct window_mode window_customize_mode = { |
| .name = "options-mode", |
| .default_format = WINDOW_CUSTOMIZE_DEFAULT_FORMAT, |
| |
| .init = window_customize_init, |
| .free = window_customize_free, |
| .resize = window_customize_resize, |
| .key = window_customize_key, |
| }; |
| |
| enum window_customize_scope { |
| WINDOW_CUSTOMIZE_NONE, |
| WINDOW_CUSTOMIZE_SERVER, |
| WINDOW_CUSTOMIZE_GLOBAL_SESSION, |
| WINDOW_CUSTOMIZE_SESSION, |
| WINDOW_CUSTOMIZE_GLOBAL_WINDOW, |
| WINDOW_CUSTOMIZE_WINDOW, |
| WINDOW_CUSTOMIZE_PANE |
| }; |
| |
| struct window_customize_itemdata { |
| struct window_customize_modedata *data; |
| enum window_customize_scope scope; |
| struct options *oo; |
| char *name; |
| int idx; |
| }; |
| |
| struct window_customize_modedata { |
| struct window_pane *wp; |
| int dead; |
| int references; |
| |
| struct mode_tree_data *data; |
| char *format; |
| int hide_global; |
| |
| struct window_customize_itemdata **item_list; |
| u_int item_size; |
| |
| struct cmd_find_state fs; |
| }; |
| |
| static uint64_t |
| window_customize_get_tag(struct options_entry *o, int idx, |
| const struct options_table_entry *oe) |
| { |
| uint64_t offset; |
| |
| if (oe == NULL) |
| return ((uint64_t)o); |
| offset = ((char *)oe - (char *)options_table) / sizeof *options_table; |
| return ((offset << 32) | ((idx + 1) << 1) | 1); |
| } |
| |
| static struct options * |
| window_customize_get_tree(enum window_customize_scope scope, |
| struct cmd_find_state *fs) |
| { |
| switch (scope) { |
| case WINDOW_CUSTOMIZE_NONE: |
| return (NULL); |
| case WINDOW_CUSTOMIZE_SERVER: |
| return (global_options); |
| case WINDOW_CUSTOMIZE_GLOBAL_SESSION: |
| return (global_s_options); |
| case WINDOW_CUSTOMIZE_SESSION: |
| return (fs->s->options); |
| case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: |
| return (global_w_options); |
| case WINDOW_CUSTOMIZE_WINDOW: |
| return (fs->w->options); |
| case WINDOW_CUSTOMIZE_PANE: |
| return (fs->wp->options); |
| } |
| return (NULL); |
| } |
| |
| static int |
| window_customize_check_item(struct window_customize_modedata *data, |
| struct window_customize_itemdata *item, struct cmd_find_state *fsp) |
| { |
| struct cmd_find_state fs; |
| |
| if (fsp == NULL) |
| fsp = &fs; |
| |
| if (cmd_find_valid_state(&data->fs)) |
| cmd_find_copy_state(fsp, &data->fs); |
| else |
| cmd_find_from_pane(fsp, data->wp, 0); |
| return (item->oo == window_customize_get_tree(item->scope, fsp)); |
| } |
| |
| static char * |
| window_customize_scope_text(enum window_customize_scope scope, |
| struct cmd_find_state *fs) |
| { |
| char *s; |
| u_int idx; |
| |
| switch (scope) { |
| case WINDOW_CUSTOMIZE_NONE: |
| case WINDOW_CUSTOMIZE_SERVER: |
| case WINDOW_CUSTOMIZE_GLOBAL_SESSION: |
| case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: |
| s = xstrdup(""); |
| break; |
| case WINDOW_CUSTOMIZE_PANE: |
| window_pane_index(fs->wp, &idx); |
| xasprintf(&s, "pane %u", idx); |
| break; |
| case WINDOW_CUSTOMIZE_SESSION: |
| xasprintf(&s, "session %s", fs->s->name); |
| break; |
| case WINDOW_CUSTOMIZE_WINDOW: |
| xasprintf(&s, "window %u", fs->wl->idx); |
| break; |
| } |
| return (s); |
| } |
| |
| static struct window_customize_itemdata * |
| window_customize_add_item(struct window_customize_modedata *data) |
| { |
| struct window_customize_itemdata *item; |
| |
| data->item_list = xreallocarray(data->item_list, data->item_size + 1, |
| sizeof *data->item_list); |
| item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); |
| return (item); |
| } |
| |
| static void |
| window_customize_free_item(struct window_customize_itemdata *item) |
| { |
| free(item->name); |
| free(item); |
| } |
| |
| static void |
| window_customize_build_array(struct window_customize_modedata *data, |
| struct mode_tree_item *top, enum window_customize_scope scope, |
| struct options_entry *o, struct format_tree *ft) |
| { |
| const struct options_table_entry *oe = options_table_entry(o); |
| struct options *oo = options_owner(o); |
| struct window_customize_itemdata *item; |
| struct options_array_item *ai; |
| char *name, *value, *text; |
| u_int idx; |
| uint64_t tag; |
| |
| ai = options_array_first(o); |
| while (ai != NULL) { |
| idx = options_array_item_index(ai); |
| |
| xasprintf(&name, "%s[%u]", options_name(o), idx); |
| format_add(ft, "option_name", "%s", name); |
| value = options_to_string(o, idx, 0); |
| format_add(ft, "option_value", "%s", value); |
| |
| item = window_customize_add_item(data); |
| item->scope = scope; |
| item->oo = oo; |
| item->name = xstrdup(options_name(o)); |
| item->idx = idx; |
| |
| text = format_expand(ft, data->format); |
| tag = window_customize_get_tag(o, idx, oe); |
| mode_tree_add(data->data, top, item, tag, name, text, -1); |
| free(text); |
| |
| free(name); |
| free(value); |
| |
| ai = options_array_next(ai); |
| } |
| } |
| |
| static void |
| window_customize_build_option(struct window_customize_modedata *data, |
| struct mode_tree_item *top, enum window_customize_scope scope, |
| struct options_entry *o, struct format_tree *ft, |
| const char *filter, struct cmd_find_state *fs) |
| { |
| const struct options_table_entry *oe = options_table_entry(o); |
| struct options *oo = options_owner(o); |
| const char *name = options_name(o); |
| struct window_customize_itemdata *item; |
| char *text, *expanded, *value; |
| int global = 0, array = 0; |
| uint64_t tag; |
| |
| if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK)) |
| return; |
| if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) |
| array = 1; |
| |
| if (scope == WINDOW_CUSTOMIZE_SERVER || |
| scope == WINDOW_CUSTOMIZE_GLOBAL_SESSION || |
| scope == WINDOW_CUSTOMIZE_GLOBAL_WINDOW) |
| global = 1; |
| if (data->hide_global && global) |
| return; |
| |
| format_add(ft, "option_name", "%s", name); |
| format_add(ft, "option_is_global", "%d", global); |
| format_add(ft, "option_is_array", "%d", array); |
| |
| text = window_customize_scope_text(scope, fs); |
| format_add(ft, "option_scope", "%s", text); |
| free(text); |
| |
| if (oe != NULL && oe->unit != NULL) |
| format_add(ft, "option_unit", "%s", oe->unit); |
| else |
| format_add(ft, "option_unit", ""); |
| |
| if (!array) { |
| value = options_to_string(o, -1, 0); |
| format_add(ft, "option_value", "%s", value); |
| free(value); |
| } |
| |
| if (filter != NULL) { |
| expanded = format_expand(ft, filter); |
| if (!format_true(expanded)) { |
| free(expanded); |
| return; |
| } |
| free(expanded); |
| } |
| item = window_customize_add_item(data); |
| item->oo = oo; |
| item->scope = scope; |
| item->name = xstrdup(name); |
| item->idx = -1; |
| |
| if (array) |
| text = NULL; |
| else |
| text = format_expand(ft, data->format); |
| tag = window_customize_get_tag(o, -1, oe); |
| top = mode_tree_add(data->data, top, item, tag, name, text, 0); |
| free(text); |
| |
| if (array) |
| window_customize_build_array(data, top, scope, o, ft); |
| } |
| |
| static void |
| window_customize_find_user_options(struct options *oo, const char ***list, |
| u_int *size) |
| { |
| struct options_entry *o; |
| const char *name; |
| u_int i; |
| |
| o = options_first(oo); |
| while (o != NULL) { |
| name = options_name(o); |
| if (*name != '@') { |
| o = options_next(o); |
| continue; |
| } |
| for (i = 0; i < *size; i++) { |
| if (strcmp((*list)[i], name) == 0) |
| break; |
| } |
| if (i != *size) { |
| o = options_next(o); |
| continue; |
| } |
| *list = xreallocarray(*list, (*size) + 1, sizeof **list); |
| (*list)[(*size)++] = name; |
| |
| o = options_next(o); |
| } |
| } |
| |
| static void |
| window_customize_build_options(struct window_customize_modedata *data, |
| const char *title, uint64_t tag, |
| enum window_customize_scope scope0, struct options *oo0, |
| enum window_customize_scope scope1, struct options *oo1, |
| enum window_customize_scope scope2, struct options *oo2, |
| struct format_tree *ft, const char *filter, struct cmd_find_state *fs) |
| { |
| struct mode_tree_item *top; |
| struct options_entry *o, *loop; |
| const char **list = NULL, *name; |
| u_int size = 0, i; |
| enum window_customize_scope scope; |
| |
| top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0); |
| |
| /* |
| * We get the options from the first tree, but build it using the |
| * values from the other two. Any tree can have user options so we need |
| * to build a separate list of them. |
| */ |
| |
| window_customize_find_user_options(oo0, &list, &size); |
| if (oo1 != NULL) |
| window_customize_find_user_options(oo1, &list, &size); |
| if (oo2 != NULL) |
| window_customize_find_user_options(oo2, &list, &size); |
| |
| for (i = 0; i < size; i++) { |
| if (oo2 != NULL) |
| o = options_get(oo0, list[i]); |
| else if (oo1 != NULL) |
| o = options_get(oo1, list[i]); |
| else |
| o = options_get(oo2, list[i]); |
| if (options_owner(o) == oo0) |
| scope = scope0; |
| else if (options_owner(o) == oo1) |
| scope = scope1; |
| else if (options_owner(o) == oo2) |
| scope = scope2; |
| window_customize_build_option(data, top, scope, o, ft, filter, |
| fs); |
| } |
| free(list); |
| |
| loop = options_first(oo0); |
| while (loop != NULL) { |
| name = options_name(loop); |
| if (*name == '@') { |
| loop = options_next(loop); |
| continue; |
| } |
| if (oo2 != NULL) |
| o = options_get(oo2, name); |
| else if (oo1 != NULL) |
| o = options_get(oo1, name); |
| else |
| o = loop; |
| if (options_owner(o) == oo0) |
| scope = scope0; |
| else if (options_owner(o) == oo1) |
| scope = scope1; |
| else if (options_owner(o) == oo2) |
| scope = scope2; |
| window_customize_build_option(data, top, scope, o, ft, filter, |
| fs); |
| loop = options_next(loop); |
| } |
| } |
| |
| static void |
| window_customize_build(void *modedata, |
| __unused struct mode_tree_sort_criteria *sort_crit, __unused uint64_t *tag, |
| const char *filter) |
| { |
| struct window_customize_modedata *data = modedata; |
| struct cmd_find_state fs; |
| struct format_tree *ft; |
| u_int i; |
| |
| for (i = 0; i < data->item_size; i++) |
| window_customize_free_item(data->item_list[i]); |
| free(data->item_list); |
| data->item_list = NULL; |
| data->item_size = 0; |
| |
| if (cmd_find_valid_state(&data->fs)) |
| cmd_find_copy_state(&fs, &data->fs); |
| else |
| cmd_find_from_pane(&fs, data->wp, 0); |
| |
| ft = format_create_from_state(NULL, NULL, &fs); |
| |
| window_customize_build_options(data, "Server Options", |
| (1ULL << 63)|OPTIONS_TABLE_SERVER, |
| WINDOW_CUSTOMIZE_SERVER, global_options, |
| WINDOW_CUSTOMIZE_NONE, NULL, |
| WINDOW_CUSTOMIZE_NONE, NULL, |
| ft, filter, &fs); |
| window_customize_build_options(data, "Session Options", |
| (1ULL << 63)|OPTIONS_TABLE_SESSION, |
| WINDOW_CUSTOMIZE_GLOBAL_SESSION, global_s_options, |
| WINDOW_CUSTOMIZE_SESSION, fs.s->options, |
| WINDOW_CUSTOMIZE_NONE, NULL, |
| ft, filter, &fs); |
| window_customize_build_options(data, "Window & Pane Options", |
| (1ULL << 63)|OPTIONS_TABLE_WINDOW, |
| WINDOW_CUSTOMIZE_GLOBAL_WINDOW, global_w_options, |
| WINDOW_CUSTOMIZE_WINDOW, fs.w->options, |
| WINDOW_CUSTOMIZE_PANE, fs.wp->options, |
| ft, filter, &fs); |
| |
| format_free(ft); |
| } |
| |
| static void |
| window_customize_draw(void *modedata, void *itemdata, |
| struct screen_write_ctx *ctx, u_int sx, u_int sy) |
| { |
| struct window_customize_modedata *data = modedata; |
| struct window_customize_itemdata *item = itemdata; |
| struct screen *s = ctx->s; |
| u_int cx = s->cx, cy = s->cy; |
| int idx; |
| struct options_entry *o, *parent; |
| struct options *go, *wo; |
| const struct options_table_entry *oe; |
| struct grid_cell gc; |
| const char **choice, *text, *name; |
| const char *space = "", *unit = ""; |
| char *value = NULL, *expanded; |
| char *default_value = NULL; |
| char choices[256] = ""; |
| struct cmd_find_state fs; |
| struct format_tree *ft; |
| |
| if (item == NULL || !window_customize_check_item(data, item, &fs)) |
| return; |
| name = item->name; |
| idx = item->idx; |
| |
| o = options_get(item->oo, name); |
| if (o == NULL) |
| return; |
| oe = options_table_entry(o); |
| |
| if (oe != NULL && oe->unit != NULL) { |
| space = " "; |
| unit = oe->unit; |
| } |
| ft = format_create_from_state(NULL, NULL, &fs); |
| |
| if (oe == NULL) |
| text = "This is a user option."; |
| else if (oe->text == NULL) |
| text = "This option doesn't have a description."; |
| else |
| text = oe->text; |
| screen_write_text(ctx, sx, sy, &grid_default_cell, "%s", text); |
| if (s->cy >= cy + sy - 1) |
| goto out; |
| |
| if (oe == NULL) |
| text = "user"; |
| else if ((oe->scope & (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) == |
| (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) |
| text = "window and pane"; |
| else if (oe->scope & OPTIONS_TABLE_WINDOW) |
| text = "window"; |
| else if (oe->scope & OPTIONS_TABLE_SESSION) |
| text = "session"; |
| else |
| text = "server"; |
| screen_write_text(ctx, sx, sy - (s->cy - cy), &grid_default_cell, |
| "This is a %s option.", text); |
| if (s->cy > cy + sy - 1) |
| goto out; |
| if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { |
| if (idx != -1) { |
| screen_write_text(ctx, sx, sy - (s->cy - cy), |
| &grid_default_cell, |
| "This is an array option, index %u.", idx); |
| } else { |
| screen_write_text(ctx, sx, sy - (s->cy - cy), |
| &grid_default_cell, "This is an array option."); |
| } |
| if (idx == -1) |
| goto out; |
| if (s->cy > cy + sy - 1) |
| goto out; |
| } |
| |
| value = options_to_string(o, idx, 0); |
| if (oe != NULL && idx == -1) { |
| default_value = options_default_to_string(oe); |
| if (strcmp(default_value, value) == 0) { |
| free(default_value); |
| default_value = NULL; |
| } |
| } |
| screen_write_nputs(ctx, sx, &grid_default_cell, "Option value: %s%s%s", |
| value, space, unit); |
| screen_write_cursormove(ctx, cx, s->cy + 1, 0); |
| if (s->cy > cy + sy - 1) |
| goto out; |
| |
| if (oe == NULL || oe->type == OPTIONS_TABLE_STRING) { |
| expanded = format_expand(ft, value); |
| if (strcmp(expanded, value) != 0) { |
| screen_write_nputs(ctx, sx, &grid_default_cell, |
| "This expands to: %s", expanded); |
| screen_write_cursormove(ctx, cx, s->cy + 1, 0); |
| if (s->cy > cy + sy - 1) |
| goto out; |
| } |
| free(expanded); |
| } |
| if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) { |
| for (choice = oe->choices; *choice != NULL; choice++) { |
| strlcat(choices, *choice, sizeof choices); |
| strlcat(choices, ", ", sizeof choices); |
| } |
| choices[strlen(choices) - 2] = '\0'; |
| screen_write_nputs(ctx, sx, &grid_default_cell, |
| "Available values are: %s", choices); |
| screen_write_cursormove(ctx, cx, s->cy + 1, 0); |
| if (s->cy > cy + sy - 1) |
| goto out; |
| } |
| if (oe != NULL && oe->type == OPTIONS_TABLE_COLOUR) { |
| screen_write_nputs(ctx, sx, &grid_default_cell, |
| "This is a colour option: "); |
| if (sx > 24) { |
| memcpy(&gc, &grid_default_cell, sizeof gc); |
| gc.fg = options_get_number(item->oo, name); |
| screen_write_nputs(ctx, sx - 24, &gc, "EXAMPLE"); |
| } |
| screen_write_cursormove(ctx, cx, s->cy + 1, 0); |
| if (s->cy > cy + sy - 1) |
| goto out; |
| } |
| if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_STYLE)) { |
| screen_write_nputs(ctx, sx, &grid_default_cell, |
| "This is a style option: "); |
| if (sx > 24) { |
| style_apply(&gc, item->oo, name, ft); |
| screen_write_nputs(ctx, sx - 24, &gc, "EXAMPLE"); |
| } |
| screen_write_cursormove(ctx, cx, s->cy + 1, 0); |
| if (s->cy > cy + sy - 1) |
| goto out; |
| } |
| if (default_value != NULL) { |
| screen_write_nputs(ctx, sx, &grid_default_cell, |
| "The default is: %s%s%s", default_value, space, unit); |
| screen_write_cursormove(ctx, cx, s->cy + 1, 0); |
| if (s->cy > cy + sy - 1) |
| goto out; |
| } |
| |
| screen_write_cursormove(ctx, cx, s->cy + 1, 0); |
| if (s->cy > cy + sy - 1) |
| goto out; |
| if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { |
| wo = NULL; |
| go = NULL; |
| } else { |
| switch (item->scope) { |
| case WINDOW_CUSTOMIZE_PANE: |
| wo = options_get_parent(item->oo); |
| go = options_get_parent(wo); |
| break; |
| case WINDOW_CUSTOMIZE_WINDOW: |
| case WINDOW_CUSTOMIZE_SESSION: |
| wo = NULL; |
| go = options_get_parent(item->oo); |
| break; |
| default: |
| wo = NULL; |
| go = NULL; |
| break; |
| } |
| } |
| if (wo != NULL && options_owner(o) != wo) { |
| parent = options_get_only(wo, name); |
| if (parent != NULL) { |
| value = options_to_string(parent, -1 , 0); |
| screen_write_nputs(ctx, sx, &grid_default_cell, |
| "Window value (from window %u): %s%s%s", fs.wl->idx, |
| value, space, unit); |
| screen_write_cursormove(ctx, cx, s->cy + 1, 0); |
| if (s->cy > cy + sy - 1) |
| goto out; |
| } |
| } |
| if (go != NULL && options_owner(o) != go) { |
| parent = options_get_only(go, name); |
| if (parent != NULL) { |
| value = options_to_string(parent, -1 , 0); |
| screen_write_nputs(ctx, sx, &grid_default_cell, |
| "Global value: %s%s%s", value, space, unit); |
| screen_write_cursormove(ctx, cx, s->cy + 1, 0); |
| if (s->cy > cy + sy - 1) |
| goto out; |
| } |
| } |
| |
| out: |
| free(value); |
| free(default_value); |
| format_free(ft); |
| } |
| |
| static void |
| window_customize_menu(void *modedata, struct client *c, key_code key) |
| { |
| struct window_customize_modedata *data = modedata; |
| struct window_pane *wp = data->wp; |
| struct window_mode_entry *wme; |
| |
| wme = TAILQ_FIRST(&wp->modes); |
| if (wme == NULL || wme->data != modedata) |
| return; |
| window_customize_key(wme, c, NULL, NULL, key, NULL); |
| } |
| |
| static u_int |
| window_customize_height(__unused void *modedata, __unused u_int height) |
| { |
| return (12); |
| } |
| |
| static struct screen * |
| window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs, |
| struct args *args) |
| { |
| struct window_pane *wp = wme->wp; |
| struct window_customize_modedata *data; |
| struct screen *s; |
| |
| wme->data = data = xcalloc(1, sizeof *data); |
| data->wp = wp; |
| data->references = 1; |
| |
| memcpy(&data->fs, fs, sizeof data->fs); |
| |
| if (args == NULL || !args_has(args, 'F')) |
| data->format = xstrdup(WINDOW_CUSTOMIZE_DEFAULT_FORMAT); |
| else |
| data->format = xstrdup(args_get(args, 'F')); |
| |
| data->data = mode_tree_start(wp, args, window_customize_build, |
| window_customize_draw, NULL, window_customize_menu, |
| window_customize_height, data, window_customize_menu_items, NULL, 0, |
| &s); |
| mode_tree_zoom(data->data, args); |
| |
| mode_tree_build(data->data); |
| mode_tree_draw(data->data); |
| |
| return (s); |
| } |
| |
| static void |
| window_customize_destroy(struct window_customize_modedata *data) |
| { |
| u_int i; |
| |
| if (--data->references != 0) |
| return; |
| |
| for (i = 0; i < data->item_size; i++) |
| window_customize_free_item(data->item_list[i]); |
| free(data->item_list); |
| |
| free(data->format); |
| |
| free(data); |
| } |
| |
| static void |
| window_customize_free(struct window_mode_entry *wme) |
| { |
| struct window_customize_modedata *data = wme->data; |
| |
| if (data == NULL) |
| return; |
| |
| data->dead = 1; |
| mode_tree_free(data->data); |
| window_customize_destroy(data); |
| } |
| |
| static void |
| window_customize_resize(struct window_mode_entry *wme, u_int sx, u_int sy) |
| { |
| struct window_customize_modedata *data = wme->data; |
| |
| mode_tree_resize(data->data, sx, sy); |
| } |
| |
| static int |
| window_customize_set_callback(struct client *c, void *itemdata, const char *s, |
| __unused int done) |
| { |
| struct window_customize_itemdata *item = itemdata; |
| struct window_customize_modedata *data = item->data; |
| struct options_entry *o; |
| const struct options_table_entry *oe; |
| struct options *oo = item->oo; |
| const char *name = item->name; |
| char *cause; |
| int idx = item->idx; |
| |
| if (s == NULL || *s == '\0' || data->dead) |
| return (0); |
| if (item == NULL || !window_customize_check_item(data, item, NULL)) |
| return (0); |
| o = options_get(oo, name); |
| if (o == NULL) |
| return (0); |
| oe = options_table_entry(o); |
| |
| if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { |
| if (idx == -1) { |
| for (idx = 0; idx < INT_MAX; idx++) { |
| if (options_array_get(o, idx) == NULL) |
| break; |
| } |
| } |
| if (options_array_set(o, idx, s, 0, &cause) != 0) |
| goto fail; |
| } else { |
| if (options_from_string(oo, oe, name, s, 0, &cause) != 0) |
| goto fail; |
| } |
| |
| options_push_changes(item->name); |
| mode_tree_build(data->data); |
| mode_tree_draw(data->data); |
| data->wp->flags |= PANE_REDRAW; |
| |
| return (0); |
| |
| fail: |
| *cause = toupper((u_char)*cause); |
| status_message_set(c, "%s", cause); |
| free(cause); |
| return (0); |
| } |
| |
| static void |
| window_customize_set_free(void *itemdata) |
| { |
| struct window_customize_itemdata *item = itemdata; |
| struct window_customize_modedata *data = item->data; |
| |
| window_customize_free_item(item); |
| window_customize_destroy(data); |
| } |
| |
| static void |
| window_customize_set_option(struct client *c, struct window_customize_modedata *data, |
| struct window_customize_itemdata *item, int global, int pane) |
| { |
| struct options_entry *o; |
| const struct options_table_entry *oe; |
| struct options *oo; |
| struct window_customize_itemdata *new_item; |
| int flag, idx = item->idx; |
| enum window_customize_scope scope; |
| u_int choice; |
| const char *name = item->name, *space = ""; |
| char *prompt, *value, *text; |
| struct cmd_find_state fs; |
| |
| if (item == NULL || !window_customize_check_item(data, item, &fs)) |
| return; |
| o = options_get(item->oo, name); |
| if (o == NULL) |
| return; |
| value = options_to_string(o, idx, 0); |
| |
| oe = options_table_entry(o); |
| if (~oe->scope & OPTIONS_TABLE_PANE) |
| pane = 0; |
| if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { |
| scope = item->scope; |
| oo = item->oo; |
| } else { |
| if (global) { |
| switch (item->scope) { |
| case WINDOW_CUSTOMIZE_NONE: |
| case WINDOW_CUSTOMIZE_SERVER: |
| case WINDOW_CUSTOMIZE_GLOBAL_SESSION: |
| case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: |
| scope = item->scope; |
| break; |
| case WINDOW_CUSTOMIZE_SESSION: |
| scope = WINDOW_CUSTOMIZE_GLOBAL_SESSION; |
| break; |
| case WINDOW_CUSTOMIZE_WINDOW: |
| case WINDOW_CUSTOMIZE_PANE: |
| scope = WINDOW_CUSTOMIZE_GLOBAL_WINDOW; |
| break; |
| } |
| } else { |
| switch (item->scope) { |
| case WINDOW_CUSTOMIZE_NONE: |
| case WINDOW_CUSTOMIZE_SERVER: |
| case WINDOW_CUSTOMIZE_SESSION: |
| scope = item->scope; |
| break; |
| case WINDOW_CUSTOMIZE_WINDOW: |
| case WINDOW_CUSTOMIZE_PANE: |
| if (pane) |
| scope = WINDOW_CUSTOMIZE_PANE; |
| else |
| scope = WINDOW_CUSTOMIZE_WINDOW; |
| break; |
| case WINDOW_CUSTOMIZE_GLOBAL_SESSION: |
| scope = WINDOW_CUSTOMIZE_SESSION; |
| break; |
| case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: |
| if (pane) |
| scope = WINDOW_CUSTOMIZE_PANE; |
| else |
| scope = WINDOW_CUSTOMIZE_WINDOW; |
| break; |
| } |
| } |
| if (scope == item->scope) |
| oo = item->oo; |
| else |
| oo = window_customize_get_tree(scope, &fs); |
| } |
| |
| if (oe != NULL && oe->type == OPTIONS_TABLE_FLAG) { |
| flag = options_get_number(oo, name); |
| options_set_number(oo, name, !flag); |
| } else if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) { |
| choice = options_get_number(oo, name); |
| if (oe->choices[choice + 1] == NULL) |
| choice = 0; |
| else |
| choice++; |
| options_set_number(oo, name, choice); |
| } else { |
| text = window_customize_scope_text(scope, &fs); |
| if (*text != '\0') |
| space = ", for "; |
| else if (scope != WINDOW_CUSTOMIZE_SERVER) |
| space = ", global"; |
| if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { |
| if (idx == -1) { |
| xasprintf(&prompt, "(%s[+]%s%s) ", name, space, |
| text); |
| } else { |
| xasprintf(&prompt, "(%s[%d]%s%s) ", name, idx, |
| space, text); |
| } |
| } else |
| xasprintf(&prompt, "(%s%s%s) ", name, space, text); |
| free(text); |
| |
| new_item = xmalloc(sizeof *new_item); |
| new_item->data = data; |
| new_item->scope = scope; |
| new_item->oo = oo; |
| new_item->name = xstrdup(name); |
| new_item->idx = idx; |
| |
| data->references++; |
| status_prompt_set(c, prompt, value, window_customize_set_callback, |
| window_customize_set_free, new_item, PROMPT_NOFORMAT); |
| } |
| free(value); |
| } |
| |
| static void |
| window_customize_unset_option(struct window_customize_modedata *data, |
| struct window_customize_itemdata *item) |
| { |
| struct options_entry *o; |
| const struct options_table_entry *oe; |
| |
| if (item == NULL || !window_customize_check_item(data, item, NULL)) |
| return; |
| |
| o = options_get(item->oo, item->name); |
| if (o == NULL) |
| return; |
| if (item->idx != -1) { |
| mode_tree_up(data->data, 0); |
| options_array_set(o, item->idx, NULL, 0, NULL); |
| return; |
| } |
| oe = options_table_entry(o); |
| if (oe != NULL && |
| options_owner(o) != global_options && |
| options_owner(o) != global_s_options && |
| options_owner(o) != global_w_options) |
| options_remove(o); |
| else |
| options_default(options_owner(o), oe); |
| } |
| |
| static void |
| window_customize_unset_each(void *modedata, void *itemdata, |
| __unused struct client *c, __unused key_code key) |
| { |
| window_customize_unset_option(modedata, itemdata); |
| } |
| |
| static void |
| window_customize_key(struct window_mode_entry *wme, struct client *c, |
| __unused struct session *s, __unused struct winlink *wl, key_code key, |
| struct mouse_event *m) |
| { |
| struct window_pane *wp = wme->wp; |
| struct window_customize_modedata *data = wme->data; |
| struct window_customize_itemdata *item, *new_item; |
| int finished; |
| |
| item = mode_tree_get_current(data->data); |
| finished = mode_tree_key(data->data, c, &key, m, NULL, NULL); |
| if (item != (new_item = mode_tree_get_current(data->data))) |
| item = new_item; |
| |
| switch (key) { |
| case '\r': |
| case 's': |
| if (item == NULL) |
| break; |
| window_customize_set_option(c, data, item, 0, 1); |
| options_push_changes(item->name); |
| mode_tree_build(data->data); |
| break; |
| case 'w': |
| if (item == NULL) |
| break; |
| window_customize_set_option(c, data, item, 0, 0); |
| options_push_changes(item->name); |
| mode_tree_build(data->data); |
| break; |
| case 'S': |
| case 'W': |
| if (item == NULL) |
| break; |
| window_customize_set_option(c, data, item, 1, 0); |
| options_push_changes(item->name); |
| mode_tree_build(data->data); |
| break; |
| case 'u': |
| if (item == NULL) |
| break; |
| window_customize_unset_option(data, item); |
| options_push_changes(item->name); |
| mode_tree_build(data->data); |
| break; |
| case 'U': |
| mode_tree_each_tagged(data->data, window_customize_unset_each, |
| c, KEYC_NONE, 1); |
| options_push_changes(item->name); |
| mode_tree_build(data->data); |
| break; |
| case 'H': |
| data->hide_global = !data->hide_global; |
| mode_tree_build(data->data); |
| break; |
| } |
| if (finished) |
| window_pane_reset_mode(wp); |
| else { |
| mode_tree_draw(data->data); |
| wp->flags |= PANE_REDRAW; |
| } |
| } |