mirror of
https://github.com/tmux/tmux.git
synced 2026-07-22 08:51:17 +00:00
additional payload (to reduce problems with lifetime of objects) and are delivered to one or more event sinks. This is more powerful and reduces the complex dependencies between control mode and hooks. Events are now used for hooks, control mode notifications and for monitors (set-hook -B). wait-for can now wait for an event to fire (-E flag, with -F to for filter), with -v to print the payload, as well as listing (-l) waiting clients on an event and forcing one to wake (-w). A few additional hooks are also now available (pane-created, pane-resized, etc) and some of the existing ones have additional format variables available.
1560 lines
41 KiB
C
1560 lines
41 KiB
C
/* $OpenBSD: window-tree.c,v 1.93 2026/07/04 22:09:06 nicm Exp $ */
|
|
|
|
/*
|
|
* 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_tree_init(struct window_mode_entry *,
|
|
struct cmd_find_state *, struct args *);
|
|
static void window_tree_free(struct window_mode_entry *);
|
|
static void window_tree_resize(struct window_mode_entry *, u_int,
|
|
u_int);
|
|
static void window_tree_update(struct window_mode_entry *);
|
|
static void window_tree_key(struct window_mode_entry *,
|
|
struct client *, struct session *,
|
|
struct winlink *, key_code, struct mouse_event *);
|
|
|
|
#define WINDOW_TREE_DEFAULT_COMMAND "switch-client -Zt '%%'"
|
|
|
|
#define WINDOW_TREE_DEFAULT_FORMAT \
|
|
"#{?pane_format," \
|
|
"#{?pane_marked,#[fg=thememagenta],}#{?pane_floating_flag,#[underscore],}" \
|
|
"#{pane_current_command}#[fg=themelightgrey]#{pane_flags}" \
|
|
"#{?#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}},: \"#{pane_title}\",}" \
|
|
",window_format," \
|
|
"#{?window_marked_flag,#[fg=thememagenta],}" \
|
|
"#{window_name}#[fg=themelightgrey]#{window_flags}" \
|
|
"#{?#{&&:#{==:#{window_panes},1},#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}}},: \"#{pane_title}\",}" \
|
|
"," \
|
|
"#[fg=themelightgrey]#{session_windows} windows" \
|
|
"#{?session_grouped, " \
|
|
"(group #{session_group}: " \
|
|
"#{session_group_list})," \
|
|
"}" \
|
|
"#{?session_attached, (attached),}" \
|
|
"}"
|
|
|
|
#define WINDOW_TREE_DEFAULT_KEY_FORMAT \
|
|
"#{?#{e|<:#{line},10}," \
|
|
"#{line}" \
|
|
",#{e|<:#{line},36}," \
|
|
"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
|
|
"}"
|
|
|
|
static const struct menu_item window_tree_menu_items[] = {
|
|
{ "Select", '\r', NULL },
|
|
{ "Expand", KEYC_RIGHT, NULL },
|
|
{ "Mark", 'm', NULL },
|
|
{ "", KEYC_NONE, NULL },
|
|
{ "Tag", 't', NULL },
|
|
{ "Tag All", '\024', NULL },
|
|
{ "Tag None", 'T', NULL },
|
|
{ "", KEYC_NONE, NULL },
|
|
{ "Kill", 'x', NULL },
|
|
{ "Kill Tagged", 'X', NULL },
|
|
{ "", KEYC_NONE, NULL },
|
|
{ "Cancel", 'q', NULL },
|
|
|
|
{ NULL, KEYC_NONE, NULL }
|
|
};
|
|
|
|
const struct window_mode window_tree_mode = {
|
|
.name = "tree-mode",
|
|
.default_format = WINDOW_TREE_DEFAULT_FORMAT,
|
|
|
|
.init = window_tree_init,
|
|
.free = window_tree_free,
|
|
.resize = window_tree_resize,
|
|
.update = window_tree_update,
|
|
.key = window_tree_key,
|
|
};
|
|
|
|
enum window_tree_type {
|
|
WINDOW_TREE_NONE,
|
|
WINDOW_TREE_SESSION,
|
|
WINDOW_TREE_WINDOW,
|
|
WINDOW_TREE_PANE,
|
|
};
|
|
|
|
struct window_tree_itemdata {
|
|
enum window_tree_type type;
|
|
int session;
|
|
int winlink;
|
|
int pane;
|
|
};
|
|
|
|
struct window_tree_modedata {
|
|
struct window_pane *wp;
|
|
int dead;
|
|
int references;
|
|
|
|
struct mode_tree_data *data;
|
|
char *format;
|
|
char *key_format;
|
|
char *command;
|
|
int squash_groups;
|
|
int hide_preview_this_pane;
|
|
int preview_is_info;
|
|
int prompt_flags;
|
|
|
|
struct window_tree_itemdata **item_list;
|
|
u_int item_size;
|
|
|
|
const char *entered;
|
|
|
|
struct cmd_find_state fs;
|
|
enum window_tree_type type;
|
|
|
|
int offset;
|
|
|
|
int left;
|
|
int right;
|
|
u_int start;
|
|
u_int end;
|
|
u_int each;
|
|
};
|
|
|
|
static enum sort_order window_tree_order_seq[] = {
|
|
SORT_INDEX,
|
|
SORT_NAME,
|
|
SORT_ACTIVITY,
|
|
SORT_Z,
|
|
SORT_END,
|
|
};
|
|
|
|
#define WINDOW_TREE_FLAG(label, cond) \
|
|
"#{?" cond ",#[fg=themegreen],#[fg=themelightgrey]}" label "#[default]"
|
|
|
|
static const char *window_tree_pane_info_lines[] = {
|
|
"#[fg=themelightgrey]Pane #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{pane_index} #[fg=themelightgrey](#{pane_id})#[default]",
|
|
"#[fg=themelightgrey]Title #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{pane_title}",
|
|
"#[fg=themelightgrey]Command #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{pane_current_command} #[fg=themelightgrey](PID #{pane_pid})#[default]",
|
|
"#[fg=themelightgrey]Path #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{pane_current_path}",
|
|
"#[fg=themelightgrey]TTY #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{pane_tty}",
|
|
"#[fg=themelightgrey]Position #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{pane_x},#{pane_y} #{pane_width}x#{pane_height}",
|
|
"#[fg=themelightgrey]Mode #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{?pane_in_mode,#{pane_mode},none}",
|
|
"#[fg=themelightgrey]Flags #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
WINDOW_TREE_FLAG("active", "pane_active") " "
|
|
WINDOW_TREE_FLAG("zoomed", "window_zoomed_flag") " "
|
|
WINDOW_TREE_FLAG("marked", "pane_marked") " "
|
|
WINDOW_TREE_FLAG("sync", "pane_synchronized") " "
|
|
WINDOW_TREE_FLAG("dead", "pane_dead") " "
|
|
WINDOW_TREE_FLAG("piped", "pane_pipe"),
|
|
};
|
|
|
|
static const char *window_tree_window_info_lines[] = {
|
|
"#[fg=themelightgrey]Window #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{window_index}: #{window_name} #[fg=themelightgrey](#{window_id})#[default]",
|
|
"#[fg=themelightgrey]Size #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{window_width}x#{window_height}",
|
|
"#[fg=themelightgrey]Panes #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{window_panes}",
|
|
"#[fg=themelightgrey]Activity Time #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{t:window_activity} #[fg=themelightgrey](#{t/r:window_activity})#[default]",
|
|
"#[fg=themelightgrey]Sessions #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{s/,/ /:window_linked_sessions_list}",
|
|
"#[fg=themelightgrey]Flags #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
WINDOW_TREE_FLAG("active", "window_active") " "
|
|
WINDOW_TREE_FLAG("last", "window_last_flag") " "
|
|
WINDOW_TREE_FLAG("bell", "window_bell_flag") " "
|
|
WINDOW_TREE_FLAG("activity", "window_activity_flag") " "
|
|
WINDOW_TREE_FLAG("silence", "window_silence_flag") " "
|
|
WINDOW_TREE_FLAG("zoomed", "window_zoomed_flag") " "
|
|
WINDOW_TREE_FLAG("marked", "window_marked_flag"),
|
|
};
|
|
|
|
static const char *window_tree_session_info_lines[] = {
|
|
"#[fg=themelightgrey]Session #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{session_name} #[fg=themelightgrey](#{session_id})#[default]",
|
|
"#[fg=themelightgrey]Created Time #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{t:session_created} #[fg=themelightgrey](#{t/r:session_created})#[default]",
|
|
"#[fg=themelightgrey]Activity Time #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{t:session_activity} #[fg=themelightgrey](#{t/r:session_activity})#[default]",
|
|
"#[fg=themelightgrey]Attached Time #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{?#{t:session_last_attached},#{t:session_last_attached} #[fg=themelightgrey](#{t/r:session_last_attached})#[default],never}",
|
|
"#[fg=themelightgrey]Clients #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{s/,/ /:session_attached_list}",
|
|
"#[fg=themelightgrey]Windows #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{session_windows}",
|
|
"#[fg=themelightgrey]Path #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{session_path}",
|
|
"#[fg=themelightgrey]Group #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
"#{?session_grouped,#{session_group} (#{session_group_size}),none}",
|
|
"#[fg=themelightgrey]Flags #[#{E:tree-mode-border-style},acs]x#[default] "
|
|
WINDOW_TREE_FLAG("attached", "session_attached") " "
|
|
WINDOW_TREE_FLAG("grouped", "session_grouped") " "
|
|
WINDOW_TREE_FLAG("marked", "session_marked") " "
|
|
WINDOW_TREE_FLAG("bell", "session_bell_flag") " "
|
|
WINDOW_TREE_FLAG("activity", "session_activity_flag") " "
|
|
WINDOW_TREE_FLAG("silence", "session_silence_flag"),
|
|
};
|
|
|
|
static void
|
|
window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
|
|
struct winlink **wlp, struct window_pane **wp)
|
|
{
|
|
*wp = NULL;
|
|
*wlp = NULL;
|
|
*sp = session_find_by_id(item->session);
|
|
if (*sp == NULL)
|
|
return;
|
|
if (item->type == WINDOW_TREE_SESSION) {
|
|
*wlp = (*sp)->curw;
|
|
*wp = (*wlp)->window->active;
|
|
return;
|
|
}
|
|
|
|
*wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
|
|
if (*wlp == NULL) {
|
|
*sp = NULL;
|
|
return;
|
|
}
|
|
if (item->type == WINDOW_TREE_WINDOW) {
|
|
*wp = (*wlp)->window->active;
|
|
return;
|
|
}
|
|
|
|
*wp = window_pane_find_by_id(item->pane);
|
|
if (!window_has_pane((*wlp)->window, *wp))
|
|
*wp = NULL;
|
|
if (*wp == NULL) {
|
|
*sp = NULL;
|
|
*wlp = NULL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static struct window_tree_itemdata *
|
|
window_tree_add_item(struct window_tree_modedata *data)
|
|
{
|
|
struct window_tree_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_tree_free_item(struct window_tree_itemdata *item)
|
|
{
|
|
free(item);
|
|
}
|
|
|
|
static void
|
|
window_tree_build_pane(struct session *s, struct winlink *wl,
|
|
struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
|
|
{
|
|
struct window_tree_modedata *data = modedata;
|
|
struct window_tree_itemdata *item;
|
|
struct mode_tree_item *mti;
|
|
char *name, *text;
|
|
u_int idx;
|
|
struct format_tree *ft;
|
|
|
|
window_pane_index(wp, &idx);
|
|
|
|
item = window_tree_add_item(data);
|
|
item->type = WINDOW_TREE_PANE;
|
|
item->session = s->id;
|
|
item->winlink = wl->idx;
|
|
item->pane = wp->id;
|
|
|
|
ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 0);
|
|
format_defaults(ft, NULL, s, wl, wp);
|
|
text = format_expand(ft, data->format);
|
|
xasprintf(&name, "%u", idx);
|
|
format_free(ft);
|
|
|
|
mti = mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text,
|
|
-1);
|
|
free(text);
|
|
free(name);
|
|
mode_tree_align(mti, 1);
|
|
}
|
|
|
|
static int
|
|
window_tree_filter_pane(struct session *s, struct winlink *wl,
|
|
struct window_pane *wp, const char *filter)
|
|
{
|
|
char *cp;
|
|
int result;
|
|
|
|
if (filter == NULL)
|
|
return (1);
|
|
|
|
cp = format_single(NULL, filter, NULL, s, wl, wp);
|
|
result = format_true(cp);
|
|
free(cp);
|
|
|
|
return (result);
|
|
}
|
|
|
|
static int
|
|
window_tree_build_window(struct session *s, struct winlink *wl,
|
|
void *modedata, struct sort_criteria *sort_crit,
|
|
struct mode_tree_item *parent, const char *filter)
|
|
{
|
|
struct window_tree_modedata *data = modedata;
|
|
struct window_tree_itemdata *item;
|
|
struct mode_tree_item *mti;
|
|
char *name, *text;
|
|
struct window_pane **l;
|
|
u_int n, i, found;
|
|
int expanded;
|
|
struct format_tree *ft;
|
|
uint64_t tag = FORMAT_NONE;
|
|
|
|
item = window_tree_add_item(data);
|
|
item->type = WINDOW_TREE_WINDOW;
|
|
item->session = s->id;
|
|
item->winlink = wl->idx;
|
|
item->pane = -1;
|
|
|
|
if (wl->window != NULL && wl->window->active != NULL)
|
|
tag = FORMAT_PANE|wl->window->active->id;
|
|
ft = format_create(NULL, NULL, tag, 0);
|
|
format_defaults(ft, NULL, s, wl, NULL);
|
|
text = format_expand(ft, data->format);
|
|
xasprintf(&name, "%u", wl->idx);
|
|
format_free(ft);
|
|
|
|
if (data->type == WINDOW_TREE_SESSION ||
|
|
data->type == WINDOW_TREE_WINDOW)
|
|
expanded = 0;
|
|
else
|
|
expanded = 1;
|
|
mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text,
|
|
expanded);
|
|
free(text);
|
|
free(name);
|
|
mode_tree_align(mti, 1);
|
|
|
|
l = sort_get_panes_window(wl->window, &n, sort_crit);
|
|
found = 0;
|
|
for (i = 0; i < n; i++) {
|
|
if (!window_tree_filter_pane(s, wl, l[i], filter))
|
|
continue;
|
|
found++;
|
|
if (data->hide_preview_this_pane && l[i] == data->wp)
|
|
continue;
|
|
window_tree_build_pane(s, wl, l[i], modedata, mti);
|
|
}
|
|
if (found == 0) {
|
|
window_tree_free_item(item);
|
|
data->item_size--;
|
|
mode_tree_remove(data->data, mti);
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
static void
|
|
window_tree_build_session(struct session *s, void *modedata,
|
|
struct sort_criteria *sort_crit, const char *filter)
|
|
{
|
|
struct window_tree_modedata *data = modedata;
|
|
struct window_tree_itemdata *item;
|
|
struct mode_tree_item *mti;
|
|
char *text;
|
|
struct winlink *wl = s->curw, **l;
|
|
u_int n, i, empty;
|
|
int expanded;
|
|
struct format_tree *ft;
|
|
uint64_t tag = FORMAT_NONE;
|
|
|
|
item = window_tree_add_item(data);
|
|
item->type = WINDOW_TREE_SESSION;
|
|
item->session = s->id;
|
|
item->winlink = -1;
|
|
item->pane = -1;
|
|
|
|
if (wl != NULL && wl->window != NULL && wl->window->active != NULL)
|
|
tag = FORMAT_PANE|wl->window->active->id;
|
|
ft = format_create(NULL, NULL, tag, 0);
|
|
format_defaults(ft, NULL, s, NULL, NULL);
|
|
text = format_expand(ft, data->format);
|
|
format_free(ft);
|
|
|
|
if (data->type == WINDOW_TREE_SESSION)
|
|
expanded = 0;
|
|
else
|
|
expanded = 1;
|
|
mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text,
|
|
expanded);
|
|
free(text);
|
|
|
|
l = sort_get_winlinks_session(s, &n, sort_crit);
|
|
empty = 0;
|
|
for (i = 0; i < n; i++) {
|
|
if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti,
|
|
filter))
|
|
empty++;
|
|
}
|
|
if (empty == n) {
|
|
window_tree_free_item(item);
|
|
data->item_size--;
|
|
mode_tree_remove(data->data, mti);
|
|
}
|
|
}
|
|
|
|
static void
|
|
window_tree_build(void *modedata, struct sort_criteria *sort_crit,
|
|
uint64_t *tag, const char *filter)
|
|
{
|
|
struct window_tree_modedata *data = modedata;
|
|
int squash_groups = data->squash_groups;
|
|
struct session *s, **l;
|
|
struct session_group *sg, *current;
|
|
u_int n, i;
|
|
|
|
current = session_group_contains(data->fs.s);
|
|
|
|
for (i = 0; i < data->item_size; i++)
|
|
window_tree_free_item(data->item_list[i]);
|
|
free(data->item_list);
|
|
data->item_list = NULL;
|
|
data->item_size = 0;
|
|
|
|
l = sort_get_sessions(&n, sort_crit);
|
|
if (n == 0)
|
|
return;
|
|
for (i = 0; i < n; i++) {
|
|
s = l[i];
|
|
if (squash_groups && (sg = session_group_contains(s)) != NULL) {
|
|
if ((sg == current && s != data->fs.s) ||
|
|
(sg != current && s != TAILQ_FIRST(&sg->sessions)))
|
|
continue;
|
|
}
|
|
window_tree_build_session(s, modedata, sort_crit, filter);
|
|
}
|
|
|
|
switch (data->type) {
|
|
case WINDOW_TREE_NONE:
|
|
break;
|
|
case WINDOW_TREE_SESSION:
|
|
*tag = (uint64_t)data->fs.s;
|
|
break;
|
|
case WINDOW_TREE_WINDOW:
|
|
*tag = (uint64_t)data->fs.wl;
|
|
break;
|
|
case WINDOW_TREE_PANE:
|
|
if (window_count_panes(data->fs.wl->window, 1) == 1)
|
|
*tag = (uint64_t)data->fs.wl;
|
|
else
|
|
*tag = (uint64_t)data->fs.wp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
|
|
u_int sx, u_int sy, const struct grid_cell *border_gc,
|
|
const struct grid_cell *label_gc, const char *label)
|
|
{
|
|
u_int width, ox, oy;
|
|
char *new_label = NULL;
|
|
|
|
if (sx < 5 || sy < 3)
|
|
return;
|
|
width = format_width(label);
|
|
if (width > sx - 4) {
|
|
label = new_label = format_trim_left(label, sx - 4);
|
|
width = format_width(new_label);
|
|
}
|
|
if (width == 0)
|
|
return;
|
|
ox = (sx - width + 1) / 2;
|
|
oy = (sy + 1) / 2;
|
|
|
|
screen_write_cursormove(ctx, px + ox - 2, py + oy - 1, 0);
|
|
screen_write_box(ctx, width + 4, 3, BOX_LINES_DEFAULT,
|
|
border_gc, NULL);
|
|
screen_write_cursormove(ctx, px + ox - 1, py + oy, 0);
|
|
screen_write_clearcharacter(ctx, width + 2, border_gc->bg);
|
|
screen_write_cursormove(ctx, px + ox, py + oy, 0);
|
|
format_draw(ctx, label_gc, width, label, NULL, 0);
|
|
free(new_label);
|
|
}
|
|
|
|
static void
|
|
window_tree_border_cell(struct grid_cell *gc, struct options *oo,
|
|
struct format_tree *ft)
|
|
{
|
|
memcpy(gc, &grid_default_cell, sizeof *gc);
|
|
style_apply(gc, oo, "tree-mode-border-style", ft);
|
|
}
|
|
|
|
static void
|
|
window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
|
|
struct screen_write_ctx *ctx, u_int sx, u_int sy)
|
|
{
|
|
struct winlink *wl;
|
|
struct window *w;
|
|
u_int cx = ctx->s->cx, cy = ctx->s->cy;
|
|
u_int loop, total, visible, each, width, offset;
|
|
u_int current, start, end, remaining, i;
|
|
struct grid_cell gc, label_gc;
|
|
int left, right;
|
|
char *label;
|
|
const char *format;
|
|
struct format_tree *ft;
|
|
struct options *oo;
|
|
|
|
total = winlink_count(&s->windows);
|
|
|
|
if (sx / total < 24) {
|
|
visible = sx / 24;
|
|
if (visible == 0)
|
|
visible = 1;
|
|
} else
|
|
visible = total;
|
|
|
|
current = 0;
|
|
RB_FOREACH(wl, winlinks, &s->windows) {
|
|
if (wl == s->curw)
|
|
break;
|
|
current++;
|
|
}
|
|
|
|
if (current < visible) {
|
|
start = 0;
|
|
end = visible;
|
|
} else if (current >= total - visible) {
|
|
start = total - visible;
|
|
end = total;
|
|
} else {
|
|
start = current - (visible / 2);
|
|
end = start + visible;
|
|
}
|
|
|
|
if (data->offset < -(int)start)
|
|
data->offset = -(int)start;
|
|
if (data->offset > (int)(total - end))
|
|
data->offset = (int)(total - end);
|
|
start += data->offset;
|
|
end += data->offset;
|
|
|
|
left = (start != 0);
|
|
right = (end != total);
|
|
if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
|
|
left = right = 0;
|
|
if (left && right) {
|
|
each = (sx - 6) / visible;
|
|
remaining = (sx - 6) - (visible * each);
|
|
} else if (left || right) {
|
|
each = (sx - 3) / visible;
|
|
remaining = (sx - 3) - (visible * each);
|
|
} else {
|
|
each = sx / visible;
|
|
remaining = sx - (visible * each);
|
|
}
|
|
if (each == 0)
|
|
return;
|
|
|
|
window_tree_border_cell(&gc, data->wp->window->options, NULL);
|
|
if (left) {
|
|
data->left = cx + 2;
|
|
screen_write_cursormove(ctx, cx + 2, cy, 0);
|
|
screen_write_vline(ctx, sy, 0, 0, &gc);
|
|
screen_write_cursormove(ctx, cx, cy + sy / 2, 0);
|
|
screen_write_puts(ctx, &gc, "<");
|
|
} else
|
|
data->left = -1;
|
|
if (right) {
|
|
data->right = cx + sx - 3;
|
|
screen_write_cursormove(ctx, cx + sx - 3, cy, 0);
|
|
screen_write_vline(ctx, sy, 0, 0, &gc);
|
|
screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0);
|
|
screen_write_puts(ctx, &gc, ">");
|
|
} else
|
|
data->right = -1;
|
|
|
|
data->start = start;
|
|
data->end = end;
|
|
data->each = each;
|
|
|
|
i = loop = 0;
|
|
RB_FOREACH(wl, winlinks, &s->windows) {
|
|
if (loop == end)
|
|
break;
|
|
if (loop < start) {
|
|
loop++;
|
|
continue;
|
|
}
|
|
w = wl->window;
|
|
oo = w->options;
|
|
|
|
ft = format_create(NULL, NULL, FORMAT_WINDOW|w->id, 0);
|
|
format_defaults(ft, NULL, s, wl, NULL);
|
|
|
|
window_tree_border_cell(&gc, oo, ft);
|
|
memcpy(&label_gc, &grid_default_cell, sizeof label_gc);
|
|
style_apply(&label_gc, oo, "tree-mode-preview-style", ft);
|
|
label_gc.bg = gc.bg;
|
|
|
|
if (left)
|
|
offset = 3 + (i * each);
|
|
else
|
|
offset = (i * each);
|
|
if (loop == end - 1)
|
|
width = each + remaining;
|
|
else
|
|
width = each - 1;
|
|
|
|
screen_write_cursormove(ctx, cx + offset, cy, 0);
|
|
screen_write_preview(ctx, &w->active->base, width, sy);
|
|
|
|
format = options_get_string(oo, "tree-mode-preview-format");
|
|
if (*format != '\0') {
|
|
label = format_expand(ft, format);
|
|
if (*label != '\0') {
|
|
window_tree_draw_label(ctx, cx + offset, cy,
|
|
width, sy, &gc, &label_gc, label);
|
|
}
|
|
free(label);
|
|
}
|
|
|
|
if (loop != end - 1) {
|
|
screen_write_cursormove(ctx, cx + offset + width, cy,
|
|
0);
|
|
screen_write_vline(ctx, sy, 0, 0, &gc);
|
|
}
|
|
loop++;
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
|
|
struct winlink *wl, struct screen_write_ctx *ctx, u_int sx, u_int sy)
|
|
{
|
|
struct window *w = wl->window;
|
|
struct window_pane *wp;
|
|
u_int cx = ctx->s->cx, cy = ctx->s->cy;
|
|
u_int loop, total, visible, each, width, offset;
|
|
u_int current, start, end, remaining, i;
|
|
struct grid_cell gc, label_gc;
|
|
int left, right;
|
|
char *label;
|
|
const char *format;
|
|
struct format_tree *ft;
|
|
struct options *oo;
|
|
|
|
total = window_count_panes(w, 1);
|
|
if (data->hide_preview_this_pane && data->wp->window == w)
|
|
total--;
|
|
if (total == 0)
|
|
return;
|
|
|
|
if (sx / total < 24) {
|
|
visible = sx / 24;
|
|
if (visible == 0)
|
|
visible = 1;
|
|
} else
|
|
visible = total;
|
|
|
|
current = 0;
|
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
|
if (data->hide_preview_this_pane && wp == data->wp)
|
|
continue;
|
|
if (wp == w->active)
|
|
break;
|
|
current++;
|
|
}
|
|
|
|
if (current < visible) {
|
|
start = 0;
|
|
end = visible;
|
|
} else if (current >= total - visible) {
|
|
start = total - visible;
|
|
end = total;
|
|
} else {
|
|
start = current - (visible / 2);
|
|
end = start + visible;
|
|
}
|
|
|
|
if (data->offset < -(int)start)
|
|
data->offset = -(int)start;
|
|
if (data->offset > (int)(total - end))
|
|
data->offset = (int)(total - end);
|
|
start += data->offset;
|
|
end += data->offset;
|
|
|
|
left = (start != 0);
|
|
right = (end != total);
|
|
if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
|
|
left = right = 0;
|
|
if (left && right) {
|
|
each = (sx - 6) / visible;
|
|
remaining = (sx - 6) - (visible * each);
|
|
} else if (left || right) {
|
|
each = (sx - 3) / visible;
|
|
remaining = (sx - 3) - (visible * each);
|
|
} else {
|
|
each = sx / visible;
|
|
remaining = sx - (visible * each);
|
|
}
|
|
if (each == 0)
|
|
return;
|
|
|
|
window_tree_border_cell(&gc, data->wp->window->options, NULL);
|
|
if (left) {
|
|
data->left = cx + 2;
|
|
screen_write_cursormove(ctx, cx + 2, cy, 0);
|
|
screen_write_vline(ctx, sy, 0, 0, &gc);
|
|
screen_write_cursormove(ctx, cx, cy + sy / 2, 0);
|
|
screen_write_puts(ctx, &gc, "<");
|
|
} else
|
|
data->left = -1;
|
|
if (right) {
|
|
data->right = cx + sx - 3;
|
|
screen_write_cursormove(ctx, cx + sx - 3, cy, 0);
|
|
screen_write_vline(ctx, sy, 0, 0, &gc);
|
|
screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0);
|
|
screen_write_puts(ctx, &gc, ">");
|
|
} else
|
|
data->right = -1;
|
|
|
|
data->start = start;
|
|
data->end = end;
|
|
data->each = each;
|
|
|
|
i = loop = 0;
|
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
|
if (data->hide_preview_this_pane && wp == data->wp)
|
|
continue;
|
|
if (loop == end)
|
|
break;
|
|
if (loop < start) {
|
|
loop++;
|
|
continue;
|
|
}
|
|
oo = wp->options;
|
|
|
|
ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 0);
|
|
format_defaults(ft, NULL, s, wl, wp);
|
|
|
|
window_tree_border_cell(&gc, oo, ft);
|
|
memcpy(&label_gc, &grid_default_cell, sizeof label_gc);
|
|
style_apply(&label_gc, oo, "tree-mode-preview-style", ft);
|
|
label_gc.bg = gc.bg;
|
|
|
|
if (left)
|
|
offset = 3 + (i * each);
|
|
else
|
|
offset = (i * each);
|
|
if (loop == end - 1)
|
|
width = each + remaining;
|
|
else
|
|
width = each - 1;
|
|
|
|
screen_write_cursormove(ctx, cx + offset, cy, 0);
|
|
screen_write_preview(ctx, &wp->base, width, sy);
|
|
|
|
format = options_get_string(oo, "tree-mode-preview-format");
|
|
if (*format != '\0') {
|
|
label = format_expand(ft, format);
|
|
if (*label != '\0') {
|
|
window_tree_draw_label(ctx, cx + offset, cy,
|
|
width, sy, &gc, &label_gc, label);
|
|
}
|
|
free(label);
|
|
}
|
|
|
|
if (loop != end - 1) {
|
|
screen_write_cursormove(ctx, cx + offset + width, cy,
|
|
0);
|
|
screen_write_vline(ctx, sy, 0, 0, &gc);
|
|
}
|
|
loop++;
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
window_tree_draw_info(struct window_tree_modedata *data, void *itemdata,
|
|
struct screen_write_ctx *ctx, u_int sx, u_int sy)
|
|
{
|
|
struct window_tree_itemdata *item = itemdata;
|
|
struct screen *s = ctx->s;
|
|
struct session *sp;
|
|
struct winlink *wl;
|
|
struct window_pane *wp;
|
|
struct grid_cell gc;
|
|
u_int cx = s->cx, cy = s->cy, i, j, k;
|
|
struct format_tree *ft;
|
|
char *expanded;
|
|
const char *const *lines[3];
|
|
u_int count[3], n = 0;
|
|
|
|
window_tree_pull_item(item, &sp, &wl, &wp);
|
|
if (sp == NULL || wp == NULL)
|
|
return;
|
|
|
|
if (item->type == WINDOW_TREE_PANE) {
|
|
lines[n] = window_tree_pane_info_lines;
|
|
count[n++] = nitems(window_tree_pane_info_lines);
|
|
}
|
|
if (item->type == WINDOW_TREE_PANE ||
|
|
item->type == WINDOW_TREE_WINDOW) {
|
|
lines[n] = window_tree_window_info_lines;
|
|
count[n++] = nitems(window_tree_window_info_lines);
|
|
}
|
|
lines[n] = window_tree_session_info_lines;
|
|
count[n++] = nitems(window_tree_session_info_lines);
|
|
|
|
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
|
|
format_defaults(ft, NULL, sp, wl, wp);
|
|
|
|
i = 0;
|
|
for (j = 0; j < n; j++) {
|
|
if (j != 0) {
|
|
if (i == sy)
|
|
break;
|
|
window_tree_border_cell(&gc, data->wp->window->options,
|
|
NULL);
|
|
screen_write_cursormove(ctx, cx, cy + i, 0);
|
|
screen_write_hline(ctx, sx, 0, 0, BOX_LINES_DEFAULT,
|
|
&gc);
|
|
if (sx > 14) {
|
|
gc.attr |= GRID_ATTR_CHARSET;
|
|
screen_write_cursormove(ctx, cx + 14, cy + i,
|
|
0);
|
|
screen_write_putc(ctx, &gc, 'n');
|
|
}
|
|
i++;
|
|
}
|
|
for (k = 0; k < count[j]; k++) {
|
|
if (i == sy)
|
|
break;
|
|
expanded = format_expand(ft, lines[j][k]);
|
|
screen_write_cursormove(ctx, cx, cy + i, 0);
|
|
format_draw(ctx, &grid_default_cell, sx, expanded, NULL,
|
|
0);
|
|
free(expanded);
|
|
i++;
|
|
}
|
|
if (i == sy)
|
|
break;
|
|
}
|
|
if (sx > 14 && i < sy) {
|
|
window_tree_border_cell(&gc, data->wp->window->options, NULL);
|
|
screen_write_cursormove(ctx, cx + 14, cy + i, 0);
|
|
screen_write_vline(ctx, sy - i, 0, 0, &gc);
|
|
}
|
|
|
|
format_free(ft);
|
|
}
|
|
|
|
static void
|
|
window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx,
|
|
u_int sx, u_int sy)
|
|
{
|
|
struct window_tree_modedata *data = modedata;
|
|
struct window_tree_itemdata *item = itemdata;
|
|
struct session *sp;
|
|
struct winlink *wl;
|
|
struct window_pane *wp;
|
|
|
|
window_tree_pull_item(item, &sp, &wl, &wp);
|
|
if (wp == NULL)
|
|
return;
|
|
|
|
if (data->preview_is_info) {
|
|
window_tree_draw_info(data, item, ctx, sx, sy);
|
|
return;
|
|
}
|
|
|
|
switch (item->type) {
|
|
case WINDOW_TREE_NONE:
|
|
break;
|
|
case WINDOW_TREE_SESSION:
|
|
window_tree_draw_session(modedata, sp, ctx, sx, sy);
|
|
break;
|
|
case WINDOW_TREE_WINDOW:
|
|
window_tree_draw_window(modedata, sp, wl, ctx, sx, sy);
|
|
break;
|
|
case WINDOW_TREE_PANE:
|
|
if (!data->hide_preview_this_pane || wp != data->wp)
|
|
screen_write_preview(ctx, &wp->base, sx, sy);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
window_tree_search(__unused void *modedata, void *itemdata, const char *ss,
|
|
int icase)
|
|
{
|
|
struct window_tree_itemdata *item = itemdata;
|
|
struct session *s;
|
|
struct winlink *wl;
|
|
struct window_pane *wp;
|
|
char *cmd;
|
|
int retval;
|
|
|
|
window_tree_pull_item(item, &s, &wl, &wp);
|
|
|
|
switch (item->type) {
|
|
case WINDOW_TREE_NONE:
|
|
return (0);
|
|
case WINDOW_TREE_SESSION:
|
|
if (s == NULL)
|
|
return (0);
|
|
if (icase)
|
|
return (strcasestr(s->name, ss) != NULL);
|
|
return (strstr(s->name, ss) != NULL);
|
|
case WINDOW_TREE_WINDOW:
|
|
if (s == NULL || wl == NULL)
|
|
return (0);
|
|
if (icase)
|
|
return (strcasestr(wl->window->name, ss) != NULL);
|
|
return (strstr(wl->window->name, ss) != NULL);
|
|
case WINDOW_TREE_PANE:
|
|
if (s == NULL || wl == NULL || wp == NULL)
|
|
break;
|
|
cmd = get_proc_name(wp->fd, wp->tty);
|
|
if (cmd == NULL || *cmd == '\0') {
|
|
free(cmd);
|
|
return (0);
|
|
}
|
|
if (icase)
|
|
retval = (strcasestr(cmd, ss) != NULL);
|
|
else
|
|
retval = (strstr(cmd, ss) != NULL);
|
|
free(cmd);
|
|
return (retval);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
window_tree_menu(void *modedata, struct client *c, key_code key)
|
|
{
|
|
struct window_tree_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_tree_key(wme, c, NULL, NULL, key, NULL);
|
|
}
|
|
|
|
static key_code
|
|
window_tree_get_key(void *modedata, void *itemdata, u_int line)
|
|
{
|
|
struct window_tree_modedata *data = modedata;
|
|
struct window_tree_itemdata *item = itemdata;
|
|
struct format_tree *ft;
|
|
struct session *s;
|
|
struct winlink *wl;
|
|
struct window_pane *wp;
|
|
char *expanded;
|
|
key_code key;
|
|
|
|
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
|
|
window_tree_pull_item(item, &s, &wl, &wp);
|
|
if (item->type == WINDOW_TREE_SESSION)
|
|
format_defaults(ft, NULL, s, NULL, NULL);
|
|
else if (item->type == WINDOW_TREE_WINDOW)
|
|
format_defaults(ft, NULL, s, wl, NULL);
|
|
else
|
|
format_defaults(ft, NULL, s, wl, wp);
|
|
format_add(ft, "line", "%u", line);
|
|
|
|
expanded = format_expand(ft, data->key_format);
|
|
key = key_string_lookup_string(expanded);
|
|
free(expanded);
|
|
format_free(ft);
|
|
return (key);
|
|
}
|
|
|
|
static int
|
|
window_tree_swap(void *cur_itemdata, void *other_itemdata,
|
|
struct sort_criteria *sort_crit)
|
|
{
|
|
struct window_tree_itemdata *cur = cur_itemdata;
|
|
struct window_tree_itemdata *other = other_itemdata;
|
|
struct session *cur_session, *other_session;
|
|
struct winlink *cur_winlink, *other_winlink;
|
|
struct window *cur_window, *other_window;
|
|
struct window_pane *cur_pane, *other_pane;
|
|
|
|
if (cur->type != other->type)
|
|
return (0);
|
|
if (cur->type != WINDOW_TREE_WINDOW)
|
|
return (0);
|
|
|
|
window_tree_pull_item(cur, &cur_session, &cur_winlink, &cur_pane);
|
|
window_tree_pull_item(other, &other_session, &other_winlink,
|
|
&other_pane);
|
|
|
|
if (cur_session != other_session)
|
|
return (0);
|
|
|
|
/*
|
|
* Swapping indexes would not swap positions in the tree, so prevent
|
|
* swapping to avoid confusing the user.
|
|
*/
|
|
if (sort_would_window_tree_swap(sort_crit, cur_winlink, other_winlink))
|
|
return (0);
|
|
|
|
other_window = other_winlink->window;
|
|
TAILQ_REMOVE(&other_window->winlinks, other_winlink, wentry);
|
|
cur_window = cur_winlink->window;
|
|
TAILQ_REMOVE(&cur_window->winlinks, cur_winlink, wentry);
|
|
|
|
other_winlink->window = cur_window;
|
|
TAILQ_INSERT_TAIL(&cur_window->winlinks, other_winlink, wentry);
|
|
cur_winlink->window = other_window;
|
|
TAILQ_INSERT_TAIL(&other_window->winlinks, cur_winlink, wentry);
|
|
|
|
if (cur_session->curw == cur_winlink)
|
|
session_set_current(cur_session, other_winlink);
|
|
else if (cur_session->curw == other_winlink)
|
|
session_set_current(cur_session, cur_winlink);
|
|
session_group_synchronize_from(cur_session);
|
|
server_redraw_session_group(cur_session);
|
|
recalculate_sizes();
|
|
|
|
return (1);
|
|
}
|
|
|
|
static void
|
|
window_tree_sort(struct sort_criteria *sort_crit)
|
|
{
|
|
sort_crit->order_seq = window_tree_order_seq;
|
|
if (sort_crit->order == SORT_END)
|
|
sort_crit->order = sort_crit->order_seq[0];
|
|
}
|
|
|
|
static const char* window_tree_help_lines[] = {
|
|
"#[fg=themelightgrey]"
|
|
" Enter #[#{E:tree-mode-border-style},acs]x#[default] Choose selected item",
|
|
"#[fg=themelightgrey]"
|
|
" S-Up #[#{E:tree-mode-border-style},acs]x#[default] Swap current and previous window",
|
|
"#[fg=themelightgrey]"
|
|
" S-Down #[#{E:tree-mode-border-style},acs]x#[default] Swap current and next window",
|
|
"#[fg=themelightgrey]"
|
|
" x #[#{E:tree-mode-border-style},acs]x#[default] Kill selected item",
|
|
"#[fg=themelightgrey]"
|
|
" X #[#{E:tree-mode-border-style},acs]x#[default] Kill tagged items",
|
|
"#[fg=themelightgrey]"
|
|
" < #[#{E:tree-mode-border-style},acs]x#[default] Scroll previews left",
|
|
"#[fg=themelightgrey]"
|
|
" > #[#{E:tree-mode-border-style},acs]x#[default] Scroll previews right",
|
|
"#[fg=themelightgrey]"
|
|
" m #[#{E:tree-mode-border-style},acs]x#[default] Set the marked pane",
|
|
"#[fg=themelightgrey]"
|
|
" M #[#{E:tree-mode-border-style},acs]x#[default] Clear the marked pane",
|
|
"#[fg=themelightgrey]"
|
|
" i #[#{E:tree-mode-border-style},acs]x#[default] Toggle session, window and pane information",
|
|
"#[fg=themelightgrey]"
|
|
" : #[#{E:tree-mode-border-style},acs]x#[default] Run a command for each tagged item",
|
|
"#[fg=themelightgrey]"
|
|
" f #[#{E:tree-mode-border-style},acs]x#[default] Enter a format",
|
|
"#[fg=themelightgrey]"
|
|
" H #[#{E:tree-mode-border-style},acs]x#[default] Jump to the starting pane",
|
|
NULL
|
|
};
|
|
|
|
static const char**
|
|
window_tree_help(u_int *width, const char **item)
|
|
{
|
|
*width = 51;
|
|
*item = "item";
|
|
return (window_tree_help_lines);
|
|
}
|
|
|
|
static struct screen *
|
|
window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
|
|
struct args *args)
|
|
{
|
|
struct window_pane *wp = wme->wp;
|
|
struct window_tree_modedata *data;
|
|
struct screen *s;
|
|
|
|
wme->data = data = xcalloc(1, sizeof *data);
|
|
data->wp = wp;
|
|
data->references = 1;
|
|
|
|
if (args_has(args, 's'))
|
|
data->type = WINDOW_TREE_SESSION;
|
|
else if (args_has(args, 'w'))
|
|
data->type = WINDOW_TREE_WINDOW;
|
|
else
|
|
data->type = WINDOW_TREE_PANE;
|
|
memcpy(&data->fs, fs, sizeof data->fs);
|
|
|
|
if (args == NULL || !args_has(args, 'F'))
|
|
data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
|
|
else
|
|
data->format = xstrdup(args_get(args, 'F'));
|
|
if (args == NULL || !args_has(args, 'K'))
|
|
data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT);
|
|
else
|
|
data->key_format = xstrdup(args_get(args, 'K'));
|
|
if (args == NULL || args_count(args) == 0)
|
|
data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
|
|
else
|
|
data->command = xstrdup(args_string(args, 0));
|
|
data->squash_groups = !args_has(args, 'G');
|
|
data->hide_preview_this_pane = args_has(args, 'h');
|
|
if (args_has(args, 'y'))
|
|
data->prompt_flags = PROMPT_ACCEPT;
|
|
|
|
data->data = mode_tree_start(wp, args, window_tree_build,
|
|
window_tree_draw, window_tree_search, window_tree_menu, NULL,
|
|
window_tree_get_key, window_tree_swap, window_tree_sort,
|
|
window_tree_help, data, window_tree_menu_items, &s);
|
|
mode_tree_zoom(data->data, args);
|
|
mode_tree_view_name(data->data, "preview");
|
|
|
|
mode_tree_build(data->data);
|
|
mode_tree_draw(data->data);
|
|
|
|
data->type = WINDOW_TREE_NONE;
|
|
|
|
return (s);
|
|
}
|
|
|
|
static void
|
|
window_tree_destroy(struct window_tree_modedata *data)
|
|
{
|
|
u_int i;
|
|
|
|
if (--data->references != 0)
|
|
return;
|
|
|
|
for (i = 0; i < data->item_size; i++)
|
|
window_tree_free_item(data->item_list[i]);
|
|
free(data->item_list);
|
|
|
|
free(data->format);
|
|
free(data->key_format);
|
|
free(data->command);
|
|
|
|
free(data);
|
|
}
|
|
|
|
static void
|
|
window_tree_free(struct window_mode_entry *wme)
|
|
{
|
|
struct window_tree_modedata *data = wme->data;
|
|
|
|
if (data == NULL)
|
|
return;
|
|
|
|
data->dead = 1;
|
|
mode_tree_free(data->data);
|
|
window_tree_destroy(data);
|
|
}
|
|
|
|
static void
|
|
window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
|
|
{
|
|
struct window_tree_modedata *data = wme->data;
|
|
|
|
mode_tree_resize(data->data, sx, sy);
|
|
}
|
|
|
|
static void
|
|
window_tree_update(struct window_mode_entry *wme)
|
|
{
|
|
struct window_tree_modedata *data = wme->data;
|
|
|
|
mode_tree_build(data->data);
|
|
mode_tree_draw(data->data);
|
|
data->wp->flags |= PANE_REDRAW;
|
|
}
|
|
|
|
static char *
|
|
window_tree_get_target(struct window_tree_itemdata *item,
|
|
struct cmd_find_state *fs)
|
|
{
|
|
struct session *s;
|
|
struct winlink *wl;
|
|
struct window_pane *wp;
|
|
char *target;
|
|
|
|
window_tree_pull_item(item, &s, &wl, &wp);
|
|
|
|
target = NULL;
|
|
switch (item->type) {
|
|
case WINDOW_TREE_NONE:
|
|
break;
|
|
case WINDOW_TREE_SESSION:
|
|
if (s == NULL)
|
|
break;
|
|
xasprintf(&target, "=%s:", s->name);
|
|
break;
|
|
case WINDOW_TREE_WINDOW:
|
|
if (s == NULL || wl == NULL)
|
|
break;
|
|
xasprintf(&target, "=%s:%u.", s->name, wl->idx);
|
|
break;
|
|
case WINDOW_TREE_PANE:
|
|
if (s == NULL || wl == NULL || wp == NULL)
|
|
break;
|
|
xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
|
|
break;
|
|
}
|
|
if (target == NULL)
|
|
cmd_find_clear_state(fs, 0);
|
|
else
|
|
cmd_find_from_winlink_pane(fs, wl, wp, 0);
|
|
return (target);
|
|
}
|
|
|
|
static void
|
|
window_tree_command_each(void *modedata, void *itemdata, struct client *c,
|
|
__unused key_code key)
|
|
{
|
|
struct window_tree_modedata *data = modedata;
|
|
struct window_tree_itemdata *item = itemdata;
|
|
char *name;
|
|
struct cmd_find_state fs;
|
|
|
|
name = window_tree_get_target(item, &fs);
|
|
if (name != NULL)
|
|
mode_tree_run_command(c, &fs, data->entered, name);
|
|
free(name);
|
|
}
|
|
|
|
static enum cmd_retval
|
|
window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
|
|
{
|
|
struct window_tree_modedata *data = modedata;
|
|
|
|
if (!data->dead) {
|
|
mode_tree_build(data->data);
|
|
mode_tree_draw(data->data);
|
|
data->wp->flags |= PANE_REDRAW;
|
|
}
|
|
window_tree_destroy(data);
|
|
return (CMD_RETURN_NORMAL);
|
|
}
|
|
|
|
static enum prompt_result
|
|
window_tree_command_callback(struct client *c, void *modedata, const char *s,
|
|
__unused enum prompt_key_result key)
|
|
{
|
|
struct window_tree_modedata *data = modedata;
|
|
|
|
if (s == NULL || *s == '\0' || data->dead)
|
|
return (PROMPT_CLOSE);
|
|
|
|
data->entered = s;
|
|
mode_tree_each_tagged(data->data, window_tree_command_each, c,
|
|
KEYC_NONE, 1);
|
|
data->entered = NULL;
|
|
|
|
data->references++;
|
|
cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
|
|
|
|
return (PROMPT_CLOSE);
|
|
}
|
|
|
|
static void
|
|
window_tree_command_free(void *modedata)
|
|
{
|
|
struct window_tree_modedata *data = modedata;
|
|
|
|
window_tree_destroy(data);
|
|
}
|
|
|
|
static void
|
|
window_tree_kill_each(__unused void *modedata, void *itemdata,
|
|
__unused struct client *c, __unused key_code key)
|
|
{
|
|
struct window_tree_itemdata *item = itemdata;
|
|
struct session *s;
|
|
struct winlink *wl;
|
|
struct window_pane *wp;
|
|
|
|
window_tree_pull_item(item, &s, &wl, &wp);
|
|
|
|
switch (item->type) {
|
|
case WINDOW_TREE_NONE:
|
|
break;
|
|
case WINDOW_TREE_SESSION:
|
|
if (s != NULL) {
|
|
server_destroy_session(s);
|
|
session_destroy(s, 1, __func__);
|
|
}
|
|
break;
|
|
case WINDOW_TREE_WINDOW:
|
|
if (wl != NULL)
|
|
server_kill_window(wl->window, 0);
|
|
break;
|
|
case WINDOW_TREE_PANE:
|
|
if (wp != NULL)
|
|
server_kill_pane(wp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static enum prompt_result
|
|
window_tree_kill_current_callback(struct client *c, void *modedata,
|
|
const char *s, __unused enum prompt_key_result key)
|
|
{
|
|
struct window_tree_modedata *data = modedata;
|
|
struct mode_tree_data *mtd = data->data;
|
|
|
|
if (s == NULL || *s == '\0' || data->dead)
|
|
return (PROMPT_CLOSE);
|
|
if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
|
|
return (PROMPT_CLOSE);
|
|
|
|
window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE);
|
|
server_renumber_all();
|
|
|
|
data->references++;
|
|
cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
|
|
|
|
return (PROMPT_CLOSE);
|
|
}
|
|
|
|
static enum prompt_result
|
|
window_tree_kill_tagged_callback(struct client *c, void *modedata,
|
|
const char *s, __unused enum prompt_key_result key)
|
|
{
|
|
struct window_tree_modedata *data = modedata;
|
|
struct mode_tree_data *mtd = data->data;
|
|
|
|
if (s == NULL || *s == '\0' || data->dead)
|
|
return (PROMPT_CLOSE);
|
|
if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
|
|
return (PROMPT_CLOSE);
|
|
|
|
mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1);
|
|
server_renumber_all();
|
|
|
|
data->references++;
|
|
cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
|
|
|
|
return (PROMPT_CLOSE);
|
|
}
|
|
|
|
static key_code
|
|
window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x,
|
|
struct window_tree_itemdata *item)
|
|
{
|
|
struct session *s;
|
|
struct winlink *wl;
|
|
struct window_pane *wp;
|
|
u_int loop;
|
|
|
|
if (key != KEYC_MOUSEDOWN1_PANE)
|
|
return (KEYC_NONE);
|
|
|
|
if (data->left != -1 && x <= (u_int)data->left)
|
|
return ('<');
|
|
if (data->right != -1 && x >= (u_int)data->right)
|
|
return ('>');
|
|
|
|
if (data->left != -1)
|
|
x -= data->left;
|
|
else if (x != 0)
|
|
x--;
|
|
if (x == 0 || data->end == 0)
|
|
x = 0;
|
|
else {
|
|
x = x / data->each;
|
|
if (data->start + x >= data->end)
|
|
x = data->end - 1;
|
|
}
|
|
|
|
window_tree_pull_item(item, &s, &wl, &wp);
|
|
if (item->type == WINDOW_TREE_SESSION) {
|
|
if (s == NULL)
|
|
return (KEYC_NONE);
|
|
mode_tree_expand_current(data->data);
|
|
loop = 0;
|
|
RB_FOREACH(wl, winlinks, &s->windows) {
|
|
if (loop == data->start + x)
|
|
break;
|
|
loop++;
|
|
}
|
|
if (wl != NULL)
|
|
mode_tree_set_current(data->data, (uint64_t)wl);
|
|
return ('\r');
|
|
}
|
|
if (item->type == WINDOW_TREE_WINDOW) {
|
|
if (wl == NULL)
|
|
return (KEYC_NONE);
|
|
mode_tree_expand_current(data->data);
|
|
loop = 0;
|
|
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
|
|
if (loop == data->start + x)
|
|
break;
|
|
loop++;
|
|
}
|
|
if (wp != NULL)
|
|
mode_tree_set_current(data->data, (uint64_t)wp);
|
|
return ('\r');
|
|
}
|
|
return (KEYC_NONE);
|
|
}
|
|
|
|
static void
|
|
window_tree_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_tree_modedata *data = wme->data;
|
|
struct window_tree_itemdata *item, *new_item;
|
|
char *name, *prompt = NULL;
|
|
struct cmd_find_state fs, *fsp = &data->fs;
|
|
int finished;
|
|
u_int tagged, x, y, idx;
|
|
struct session *ns;
|
|
struct winlink *nwl;
|
|
struct window_pane *nwp;
|
|
|
|
item = mode_tree_get_current(data->data);
|
|
finished = mode_tree_key(data->data, c, &key, m, &x, &y);
|
|
|
|
again:
|
|
if (item != (new_item = mode_tree_get_current(data->data))) {
|
|
item = new_item;
|
|
data->offset = 0;
|
|
}
|
|
if (KEYC_IS_MOUSE(key) && m != NULL) {
|
|
key = window_tree_mouse(data, key, x, item);
|
|
goto again;
|
|
}
|
|
|
|
switch (key) {
|
|
case '<':
|
|
data->offset--;
|
|
break;
|
|
case '>':
|
|
data->offset++;
|
|
break;
|
|
case 'H':
|
|
mode_tree_expand(data->data, (uint64_t)fsp->s);
|
|
mode_tree_expand(data->data, (uint64_t)fsp->wl);
|
|
if (!mode_tree_set_current(data->data, (uint64_t)wme->wp))
|
|
mode_tree_set_current(data->data, (uint64_t)fsp->wl);
|
|
break;
|
|
case 'm':
|
|
window_tree_pull_item(item, &ns, &nwl, &nwp);
|
|
server_set_marked(ns, nwl, nwp);
|
|
mode_tree_build(data->data);
|
|
break;
|
|
case 'M':
|
|
server_clear_marked();
|
|
mode_tree_build(data->data);
|
|
break;
|
|
case 'i':
|
|
data->preview_is_info = !data->preview_is_info;
|
|
if (data->preview_is_info)
|
|
mode_tree_view_name(data->data, "info");
|
|
else
|
|
mode_tree_view_name(data->data, "preview");
|
|
break;
|
|
case 'x':
|
|
window_tree_pull_item(item, &ns, &nwl, &nwp);
|
|
switch (item->type) {
|
|
case WINDOW_TREE_NONE:
|
|
break;
|
|
case WINDOW_TREE_SESSION:
|
|
if (ns == NULL)
|
|
break;
|
|
xasprintf(&prompt, "Kill session %s? ", ns->name);
|
|
break;
|
|
case WINDOW_TREE_WINDOW:
|
|
if (nwl == NULL)
|
|
break;
|
|
xasprintf(&prompt, "Kill window %u? ", nwl->idx);
|
|
break;
|
|
case WINDOW_TREE_PANE:
|
|
if (nwp == NULL || window_pane_index(nwp, &idx) != 0)
|
|
break;
|
|
xasprintf(&prompt, "Kill pane %u? ", idx);
|
|
break;
|
|
}
|
|
if (prompt == NULL)
|
|
break;
|
|
data->references++;
|
|
mode_tree_set_prompt(data->data, c, prompt, "",
|
|
PROMPT_TYPE_COMMAND,
|
|
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
|
|
window_tree_kill_current_callback, window_tree_command_free,
|
|
data);
|
|
free(prompt);
|
|
break;
|
|
case 'X':
|
|
tagged = mode_tree_count_tagged(data->data);
|
|
if (tagged == 0)
|
|
break;
|
|
xasprintf(&prompt, "Kill %u tagged? ", tagged);
|
|
data->references++;
|
|
mode_tree_set_prompt(data->data, c, prompt, "",
|
|
PROMPT_TYPE_COMMAND,
|
|
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
|
|
window_tree_kill_tagged_callback, window_tree_command_free,
|
|
data);
|
|
free(prompt);
|
|
break;
|
|
case ':':
|
|
tagged = mode_tree_count_tagged(data->data);
|
|
if (tagged != 0)
|
|
xasprintf(&prompt, "(%u tagged) ", tagged);
|
|
else
|
|
xasprintf(&prompt, "(current) ");
|
|
data->references++;
|
|
mode_tree_set_prompt(data->data, c, prompt, "",
|
|
PROMPT_TYPE_COMMAND, PROMPT_NOFORMAT,
|
|
window_tree_command_callback, window_tree_command_free,
|
|
data);
|
|
free(prompt);
|
|
break;
|
|
case '\r':
|
|
name = window_tree_get_target(item, &fs);
|
|
if (name != NULL)
|
|
mode_tree_run_command(c, NULL, data->command, name);
|
|
finished = 1;
|
|
free(name);
|
|
break;
|
|
}
|
|
if (finished)
|
|
window_pane_reset_mode(wp);
|
|
else {
|
|
mode_tree_draw(data->data);
|
|
wp->flags |= PANE_REDRAW;
|
|
}
|
|
}
|