diff options
Diffstat (limited to 'window-customize.c')
-rw-r--r-- | window-customize.c | 1013 |
1 files changed, 1013 insertions, 0 deletions
diff --git a/window-customize.c b/window-customize.c new file mode 100644 index 00000000..93b2e840 --- /dev/null +++ b/window-customize.c @@ -0,0 +1,1013 @@ +/* $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; + } +} |