summaryrefslogtreecommitdiffstats
path: root/window-customize.c
diff options
context:
space:
mode:
authornicm <nicm>2020-05-16 16:02:24 +0000
committernicm <nicm>2020-05-16 16:02:24 +0000
commitd67245c734c9c600ad6d186570a1230aa21b80c8 (patch)
tree9e71286a88716251c09d3dd2c2b323074f9f40ff /window-customize.c
parent472d77fd0f4af8431267473df3cf109030760fa1 (diff)
Add a customize mode where keys and options may be browsed and changed,
includes adding a brief description of each option. Bound to "C" by default.
Diffstat (limited to 'window-customize.c')
-rw-r--r--window-customize.c1430
1 files changed, 1430 insertions, 0 deletions
diff --git a/window-customize.c b/window-customize.c
new file mode 100644
index 00000000..2d82897f
--- /dev/null
+++ b/window-customize.c
@@ -0,0 +1,1430 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2020 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 \
+ "#{?is_option," \
+ "#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \
+ "#[ignore]" \
+ "#{option_value}#{?option_unit, #{option_unit},}" \
+ "," \
+ "#{key}" \
+ "}"
+
+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_KEY,
+ 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;
+
+ char *table;
+ key_code key;
+
+ 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 ((2ULL << 62)|(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:
+ case WINDOW_CUSTOMIZE_KEY:
+ 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 int
+window_customize_get_key(struct window_customize_itemdata *item,
+ struct key_table **ktp, struct key_binding **bdp)
+{
+ struct key_table *kt;
+ struct key_binding *bd;
+
+ kt = key_bindings_get_table(item->table, 0);
+ if (kt == NULL)
+ return (0);
+ bd = key_bindings_get(kt, item->key);
+ if (bd == NULL)
+ return (0);
+
+ if (ktp != NULL)
+ *ktp = kt;
+ if (bdp != NULL)
+ *bdp = bd;
+ return (1);
+}
+
+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_KEY:
+ 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->table);
+ 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", "%s", "");
+
+ 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) == oo2)
+ scope = scope2;
+ else if (options_owner(o) == oo1)
+ scope = scope1;
+ else
+ scope = scope0;
+ 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) == oo2)
+ scope = scope2;
+ else if (options_owner(o) == oo1)
+ scope = scope1;
+ else
+ scope = scope0;
+ window_customize_build_option(data, top, scope, o, ft, filter,
+ fs);
+ loop = options_next(loop);
+ }
+}
+
+static void
+window_customize_build_keys(struct window_customize_modedata *data,
+ struct key_table *kt, struct format_tree *ft, const char *filter,
+ struct cmd_find_state *fs, u_int number)
+{
+ struct mode_tree_item *top, *child, *mti;
+ struct window_customize_itemdata *item;
+ struct key_binding *bd;
+ char *title, *text, *tmp, *expanded;
+ const char *flag;
+ uint64_t tag;
+
+ tag = (1ULL << 62)|((uint64_t)number << 54)|1;
+
+ xasprintf(&title, "Key Table - %s", kt->name);
+ top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
+ free(title);
+
+ ft = format_create_from_state(NULL, NULL, fs);
+ format_add(ft, "is_option", "0");
+ format_add(ft, "is_key", "1");
+
+ bd = key_bindings_first(kt);
+ while (bd != NULL) {
+ format_add(ft, "key", "%s", key_string_lookup_key(bd->key));
+ if (bd->note != NULL)
+ format_add(ft, "key_note", "%s", bd->note);
+ if (filter != NULL) {
+ expanded = format_expand(ft, filter);
+ if (!format_true(expanded)) {
+ free(expanded);
+ continue;
+ }
+ free(expanded);
+ }
+
+ item = window_customize_add_item(data);
+ item->scope = WINDOW_CUSTOMIZE_KEY;
+ item->table = xstrdup(kt->name);
+ item->key = bd->key;
+
+ expanded = format_expand(ft, data->format);
+ child = mode_tree_add(data->data, top, item, (uint64_t)bd,
+ expanded, NULL, 0);
+ free(expanded);
+
+ tmp = cmd_list_print(bd->cmdlist, 0);
+ xasprintf(&text, "#[ignore]%s", tmp);
+ free(tmp);
+ mti = mode_tree_add(data->data, child, item,
+ tag|(bd->key << 3)|(0 << 1)|1, "Command", text, -1);
+ mode_tree_draw_as_parent(mti);
+ free(text);
+
+ if (bd->note != NULL)
+ xasprintf(&text, "#[ignore]%s", bd->note);
+ else
+ text = xstrdup("");
+ mti = mode_tree_add(data->data, child, item,
+ tag|(bd->key << 3)|(1 << 1)|1, "Note", text, -1);
+ mode_tree_draw_as_parent(mti);
+ free(text);
+
+ if (bd->flags & KEY_BINDING_REPEAT)
+ flag = "on";
+ else
+ flag = "off";
+ mti = mode_tree_add(data->data, child, item,
+ tag|(bd->key << 3)|(2 << 1)|1, "Repeat", flag, -1);
+ mode_tree_draw_as_parent(mti);
+
+ bd = key_bindings_next(kt, bd);
+ }
+
+ format_free(ft);
+}
+
+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;
+ struct key_table *kt;
+
+ 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);
+ format_add(ft, "is_option", "1");
+ format_add(ft, "is_key", "0");
+
+ window_customize_build_options(data, "Server Options",
+ (3ULL << 62)|(OPTIONS_TABLE_SERVER << 1)|1,
+ WINDOW_CUSTOMIZE_SERVER, global_options,
+ WINDOW_CUSTOMIZE_NONE, NULL,
+ WINDOW_CUSTOMIZE_NONE, NULL,
+ ft, filter, &fs);
+ window_customize_build_options(data, "Session Options",
+ (3ULL << 62)|(OPTIONS_TABLE_SESSION << 1)|1,
+ 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",
+ (3ULL << 62)|(OPTIONS_TABLE_WINDOW << 1)|1,
+ 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);
+ ft = format_create_from_state(NULL, NULL, &fs);
+
+ i = 0;
+ kt = key_bindings_first_table();
+ while (kt != NULL) {
+ if (!RB_EMPTY(&kt->key_bindings)) {
+ window_customize_build_keys(data, kt, ft, filter, &fs,
+ i);
+ if (++i == 256)
+ break;
+ }
+ kt = key_bindings_next_table(kt);
+ }
+
+ format_free(ft);
+}
+
+static void
+window_customize_draw_key(__unused struct window_customize_modedata *data,
+ struct window_customize_itemdata *item, struct screen_write_ctx *ctx,
+ u_int sx, u_int sy)
+{
+ struct screen *s = ctx->s;
+ u_int cx = s->cx, cy = s->cy;
+ struct key_table *kt;
+ struct key_binding *bd, *default_bd;
+ const char *note, *period = "";
+ char *cmd, *default_cmd;
+
+ if (item == NULL || !window_customize_get_key(item, &kt, &bd))
+ return;
+
+ note = bd->note;
+ if (note == NULL)
+ note = "There is no note for this key.";
+ if (*note != '\0' && note[strlen (note) - 1] != '.')
+ period = ".";
+ if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s%s",
+ note, period))
+ return;
+ screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
+ if (s->cy >= cy + sy - 1)
+ return;
+
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "This key is in the %s table.", kt->name))
+ return;
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "This key %s repeat.",
+ (bd->flags & KEY_BINDING_REPEAT) ? "does" : "does not"))
+ return;
+ screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
+ if (s->cy >= cy + sy - 1)
+ return;
+
+ cmd = cmd_list_print(bd->cmdlist, 0);
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "Command: %s", cmd)) {
+ free(cmd);
+ return;
+ }
+ default_bd = key_bindings_get_default(kt, bd->key);
+ if (default_bd != NULL) {
+ default_cmd = cmd_list_print(default_bd->cmdlist, 0);
+ if (strcmp(cmd, default_cmd) != 0 &&
+ !screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "The default is: %s", default_cmd)) {
+ free(default_cmd);
+ free(cmd);
+ return;
+ }
+ free(default_cmd);
+ }
+ free(cmd);
+}
+
+static void
+window_customize_draw_option(struct window_customize_modedata *data,
+ struct window_customize_itemdata *item, struct screen_write_ctx *ctx,
+ u_int sx, u_int sy)
+{
+ 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 (!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;
+ if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s",
+ text))
+ goto out;
+ screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
+ 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";
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "This is a %s option.", text))
+ goto out;
+ if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
+ if (idx != -1) {
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
+ 0, &grid_default_cell,
+ "This is an array option, index %u.", idx))
+ goto out;
+ } else {
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
+ 0, &grid_default_cell, "This is an array option."))
+ goto out;
+ }
+ if (idx == -1)
+ goto out;
+ }
+ screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
+ 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;
+ }
+ }
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "Option value: %s%s%s", value, space, unit))
+ goto out;
+ if (oe == NULL || oe->type == OPTIONS_TABLE_STRING) {
+ expanded = format_expand(ft, value);
+ if (strcmp(expanded, value) != 0) {
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
+ 0, &grid_default_cell, "This expands to: %s",
+ expanded))
+ 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';
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "Available values are: %s",
+ choices))
+ goto out;
+ }
+ if (oe != NULL && oe->type == OPTIONS_TABLE_COLOUR) {
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
+ &grid_default_cell, "This is a colour option: "))
+ goto out;
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ gc.fg = options_get_number(item->oo, name);
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
+ "EXAMPLE"))
+ goto out;
+ }
+ if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_STYLE)) {
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
+ &grid_default_cell, "This is a style option: "))
+ goto out;
+ style_apply(&gc, item->oo, name, ft);
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
+ "EXAMPLE"))
+ goto out;
+ }
+ if (default_value != NULL) {
+ if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
+ &grid_default_cell, "The default is: %s%s%s", default_value,
+ space, unit))
+ goto out;
+ }
+
+ screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
+ 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);
+ if (!screen_write_text(ctx, s->cx, sx,
+ sy - (s->cy - cy), 0, &grid_default_cell,
+ "Window value (from window %u): %s%s%s", fs.wl->idx,
+ value, space, unit))
+ 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);
+ if (!screen_write_text(ctx, s->cx, sx,
+ sy - (s->cy - cy), 0, &grid_default_cell,
+ "Global value: %s%s%s", value, space, unit))
+ goto out;
+ }
+ }
+
+out:
+ free(value);
+ free(default_value);
+ 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;
+
+ if (item == NULL)
+ return;
+
+ if (item->scope == WINDOW_CUSTOMIZE_KEY)
+ window_customize_draw_key(data, item, ctx, sx, sy);
+ else
+ window_customize_draw_option(data, item, ctx, sx, sy);
+}
+
+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 void
+window_customize_free_callback(void *modedata)
+{
+ window_customize_destroy(modedata);
+}
+
+static void
+window_customize_free_item_callback(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 int
+window_customize_set_option_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, 1, "%s", cause);
+ free(cause);
+ return (0);
+}
+
+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;
+
+ 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_KEY:
+ 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_KEY:
+ 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);
+
+ value = options_to_string(o, idx, 0);
+
+ new_item = xcalloc(1, 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_option_callback,
+ window_customize_free_item_callback, new_item,
+ PROMPT_NOFORMAT);
+
+ free(prompt);
+ 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) {
+ if (item == mode_tree_get_current(data->data))
+ 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 int
+window_customize_set_command_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 key_binding *bd;
+ struct cmd_parse_result *pr;
+ char *error;
+
+ if (s == NULL || *s == '\0' || data->dead)</