mirror of
https://github.com/tmux/tmux.git
synced 2024-11-16 17:39:09 +00:00
1014 lines
26 KiB
C
1014 lines
26 KiB
C
|
/* $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;
|
||
|
}
|
||
|
}
|