Rewrite of choose mode, both to simplify and tidy the code and to add

some modern features.

Now the common code is in mode-tree.c, which provides an API used by the
three modes now separated into window-{buffer,client,tree}.c. Buffer
mode shows buffers, client mode clients and tree mode a tree of
sessions, windows and panes.

Each mode has a common set of key bindings plus a few that are specific
to the mode. Other changes are:

- each mode has a preview pane: for buffers this is the buffer content
  (very useful), for others it is a preview of the pane;

- items may be sorted in different ways ('O' key);

- multiple items may be tagged and an operation applied to all of them
  (for example, to delete multiple buffers at once);

- in tree mode a command may be run on the selected item (session,
  window, pane) or on tagged items (key ':');

- displayed items may be filtered in tree mode by using a format (this
  is used to implement find-window) (key 'f');

- the custom format (-F) for the display is no longer available;

- shortcut keys change from 0-9, a-z, A-Z which was always a bit weird
  with keys used for other uses to 0-9, M-a to M-z.

Now that the code is simpler, other improvements will come later.

Primary key bindings for each mode are documented under the commands in
the man page (choose-buffer, choose-client, choose-tree).

Parts written by Thomas Adam.
pull/348/merge
nicm 2017-05-30 21:44:59 +00:00
parent bd39fcbeea
commit aad4e4ddb1
26 changed files with 2434 additions and 1905 deletions

View File

@ -10,8 +10,6 @@ SRCS= alerts.c \
cmd-bind-key.c \
cmd-break-pane.c \
cmd-capture-pane.c \
cmd-choose-buffer.c \
cmd-choose-client.c \
cmd-choose-tree.c \
cmd-command-prompt.c \
cmd-confirm-before.c \
@ -20,6 +18,7 @@ SRCS= alerts.c \
cmd-display-message.c \
cmd-display-panes.c \
cmd-find-window.c \
cmd-find.c \
cmd-if-shell.c \
cmd-join-pane.c \
cmd-kill-pane.c \
@ -40,6 +39,7 @@ SRCS= alerts.c \
cmd-new-window.c \
cmd-paste-buffer.c \
cmd-pipe-pane.c \
cmd-queue.c \
cmd-refresh-client.c \
cmd-rename-session.c \
cmd-rename-window.c \
@ -69,11 +69,9 @@ SRCS= alerts.c \
cmd-unbind-key.c \
cmd-wait-for.c \
cmd.c \
cmd-find.c \
cmd-queue.c \
colour.c \
control.c \
control-notify.c \
control.c \
environ.c \
format.c \
grid-view.c \
@ -88,6 +86,7 @@ SRCS= alerts.c \
layout-set.c \
layout.c \
log.c \
mode-tree.c \
names.c \
notify.c \
options-table.c \
@ -112,9 +111,11 @@ SRCS= alerts.c \
tty-term.c \
tty.c \
utf8.c \
window-choose.c \
window-buffer.c \
window-client.c \
window-clock.c \
window-copy.c \
window-tree.c \
window.c \
xmalloc.c \
xterm-keys.c

View File

@ -35,7 +35,6 @@ struct args_entry {
RB_ENTRY(args_entry) entry;
};
static void args_set(struct args *, u_char, const char *);
static struct args_entry *args_find(struct args *, u_char);
static int args_cmp(struct args_entry *, struct args_entry *);
@ -196,7 +195,7 @@ args_has(struct args *args, u_char ch)
}
/* Set argument value in the arguments tree. */
static void
void
args_set(struct args *args, u_char ch, const char *value)
{
struct args_entry *entry;

3
cfg.c
View File

@ -23,7 +23,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <util.h>
#include "tmux.h"
@ -234,7 +233,7 @@ cfg_show_causes(struct session *s)
return;
wp = s->curw->window->active;
window_pane_set_mode(wp, &window_copy_mode);
window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
window_copy_init_for_output(wp);
for (i = 0; i < cfg_ncauses; i++) {
window_copy_add(wp, "%s", cfg_causes[i]);

View File

@ -1,101 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2010 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 "tmux.h"
/*
* Enter choice mode to choose a buffer.
*/
#define CHOOSE_BUFFER_TEMPLATE \
"#{buffer_name}: #{buffer_size} bytes: #{buffer_sample}"
static enum cmd_retval cmd_choose_buffer_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer",
.alias = NULL,
.args = { "F:t:", 0, 1 },
.usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
.target = { 't', CMD_FIND_WINDOW, 0 },
.flags = 0,
.exec = cmd_choose_buffer_exec
};
static enum cmd_retval
cmd_choose_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct winlink *wl = item->target.wl;
struct window_choose_data *cdata;
struct paste_buffer *pb;
char *action, *action_data;
const char *template;
u_int idx;
if (c == NULL) {
cmdq_error(item, "no client available");
return (CMD_RETURN_ERROR);
}
if ((template = args_get(args, 'F')) == NULL)
template = CHOOSE_BUFFER_TEMPLATE;
if (paste_get_top(NULL) == NULL)
return (CMD_RETURN_NORMAL);
if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
return (CMD_RETURN_NORMAL);
if (args->argc != 0)
action = xstrdup(args->argv[0]);
else
action = xstrdup("paste-buffer -b '%%'");
idx = 0;
pb = NULL;
while ((pb = paste_walk(pb)) != NULL) {
cdata = window_choose_data_create(TREE_OTHER, c, c->session);
cdata->idx = idx;
cdata->ft_template = xstrdup(template);
format_defaults_paste_buffer(cdata->ft, pb);
xasprintf(&action_data, "%s", paste_buffer_name(pb));
cdata->command = cmd_template_replace(action, action_data, 1);
free(action_data);
window_choose_add(wl->window->active, cdata);
idx++;
}
free(action);
window_choose_ready(wl->window->active, 0, NULL);
return (CMD_RETURN_NORMAL);
}

View File

@ -1,135 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 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 "tmux.h"
/*
* Enter choice mode to choose a client.
*/
#define CHOOSE_CLIENT_TEMPLATE \
"#{client_name}: #{session_name} " \
"[#{client_width}x#{client_height} #{client_termname}]" \
"#{?client_utf8, (utf8),}#{?client_readonly, (ro),} " \
"(last used #{t:client_activity})"
static enum cmd_retval cmd_choose_client_exec(struct cmd *,
struct cmdq_item *);
static void cmd_choose_client_callback(struct window_choose_data *);
const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client",
.alias = NULL,
.args = { "F:t:", 0, 1 },
.usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
.target = { 't', CMD_FIND_WINDOW, 0 },
.flags = 0,
.exec = cmd_choose_client_exec
};
struct cmd_choose_client_data {
struct client *client;
};
static enum cmd_retval
cmd_choose_client_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct client *c1;
struct window_choose_data *cdata;
struct winlink *wl = item->target.wl;
const char *template;
char *action;
u_int idx, cur;
if (c == NULL) {
cmdq_error(item, "no client available");
return (CMD_RETURN_ERROR);
}
if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
return (CMD_RETURN_NORMAL);
if ((template = args_get(args, 'F')) == NULL)
template = CHOOSE_CLIENT_TEMPLATE;
if (args->argc != 0)
action = xstrdup(args->argv[0]);
else
action = xstrdup("detach-client -t '%%'");
cur = idx = 0;
TAILQ_FOREACH(c1, &clients, entry) {
if (c1->session == NULL)
continue;
if (c1 == item->client)
cur = idx;
cdata = window_choose_data_create(TREE_OTHER, c, c->session);
cdata->idx = idx;
cdata->ft_template = xstrdup(template);
format_add(cdata->ft, "line", "%u", idx);
format_defaults(cdata->ft, c1, NULL, NULL, NULL);
cdata->command = cmd_template_replace(action, c1->name, 1);
window_choose_add(wl->window->active, cdata);
idx++;
}
free(action);
window_choose_ready(wl->window->active, cur,
cmd_choose_client_callback);
return (CMD_RETURN_NORMAL);
}
static void
cmd_choose_client_callback(struct window_choose_data *cdata)
{
struct client *c;
u_int idx;
if (cdata == NULL)
return;
if (cdata->start_client->flags & CLIENT_DEAD)
return;
idx = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (idx == cdata->idx)
break;
idx++;
}
if (c == NULL || c->session == NULL)
return;
window_choose_data_run(cdata);
}

View File

@ -18,66 +18,48 @@
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
#define CMD_CHOOSE_TREE_WINDOW_ACTION "select-window -t '%%'"
#define CMD_CHOOSE_TREE_SESSION_ACTION "switch-client -t '%%'"
/*
* Enter choice mode to choose a session and/or window.
* Enter a mode.
*/
#define CHOOSE_TREE_SESSION_TEMPLATE \
"#{session_name}: #{session_windows} windows" \
"#{?session_grouped, (group ,}" \
"#{session_group}#{?session_grouped,),}" \
"#{?session_attached, (attached),}"
#define CHOOSE_TREE_WINDOW_TEMPLATE \
"#{window_index}: #{window_name}#{window_flags} " \
"\"#{pane_title}\""
static enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree",
.alias = NULL,
.args = { "S:W:swub:c:t:", 0, 1 },
.usage = "[-suw] [-b session-template] [-c window template] "
"[-S format] [-W format] " CMD_TARGET_WINDOW_USAGE,
.args = { "st:w", 0, 1 },
.usage = "[-sw] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_choose_tree_exec
};
const struct cmd_entry cmd_choose_session_entry = {
.name = "choose-session",
const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client",
.alias = NULL,
.args = { "F:t:", 0, 1 },
.usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
.args = { "t:", 0, 1 },
.usage = CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_choose_tree_exec
};
const struct cmd_entry cmd_choose_window_entry = {
.name = "choose-window",
const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer",
.alias = NULL,
.args = { "F:t:", 0, 1 },
.usage = CMD_TARGET_WINDOW_USAGE "[-F format] [template]",
.args = { "t:", 0, 1 },
.usage = CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_choose_tree_exec
@ -87,167 +69,20 @@ static enum cmd_retval
cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct winlink *wl = item->target.wl, *wm;
struct session *s = item->target.s, *s2;
struct window_choose_data *wcd = NULL;
const char *ses_template, *win_template;
char *final_win_action, *cur_win_template;
char *final_win_template_middle;
char *final_win_template_last;
const char *ses_action, *win_action;
u_int cur_win, idx_ses, win_ses, win_max;
u_int wflag, sflag;
struct window_pane *wp = item->target.wp;
const struct window_mode *mode;
ses_template = win_template = NULL;
ses_action = win_action = NULL;
if (c == NULL) {
cmdq_error(item, "no client available");
return (CMD_RETURN_ERROR);
}
if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
return (CMD_RETURN_NORMAL);
/* Sort out which command this is. */
wflag = sflag = 0;
if (self->entry == &cmd_choose_session_entry) {
sflag = 1;
if ((ses_template = args_get(args, 'F')) == NULL)
ses_template = CHOOSE_TREE_SESSION_TEMPLATE;
if (args->argc != 0)
ses_action = args->argv[0];
else
ses_action = CMD_CHOOSE_TREE_SESSION_ACTION;
} else if (self->entry == &cmd_choose_window_entry) {
wflag = 1;
if ((win_template = args_get(args, 'F')) == NULL)
win_template = CHOOSE_TREE_WINDOW_TEMPLATE;
if (args->argc != 0)
win_action = args->argv[0];
else
win_action = CMD_CHOOSE_TREE_WINDOW_ACTION;
} else {
wflag = args_has(args, 'w');
sflag = args_has(args, 's');
if ((ses_action = args_get(args, 'b')) == NULL)
ses_action = CMD_CHOOSE_TREE_SESSION_ACTION;
if ((win_action = args_get(args, 'c')) == NULL)
win_action = CMD_CHOOSE_TREE_WINDOW_ACTION;
if ((ses_template = args_get(args, 'S')) == NULL)
ses_template = CHOOSE_TREE_SESSION_TEMPLATE;
if ((win_template = args_get(args, 'W')) == NULL)
win_template = CHOOSE_TREE_WINDOW_TEMPLATE;
}
/*
* If not asking for windows and sessions, assume no "-ws" given and
* hence display the entire tree outright.
*/
if (!wflag && !sflag)
wflag = sflag = 1;
/*
* If we're drawing in tree mode, including sessions, then pad the
* window template, otherwise just render the windows as a flat list
* without any padding.
*/
if (wflag && sflag) {
xasprintf(&final_win_template_middle,
" \001tq\001> %s", win_template);
xasprintf(&final_win_template_last,
" \001mq\001> %s", win_template);
} else if (wflag) {
final_win_template_middle = xstrdup(win_template);
final_win_template_last = xstrdup(win_template);
if (self->entry == &cmd_choose_buffer_entry) {
if (paste_get_top(NULL) == NULL)
return (CMD_RETURN_NORMAL);
mode = &window_buffer_mode;
} else if (self->entry == &cmd_choose_client_entry) {
if (server_client_how_many() == 0)
return (CMD_RETURN_NORMAL);
mode = &window_client_mode;
} else
final_win_template_middle = final_win_template_last = NULL;
idx_ses = cur_win = -1;
RB_FOREACH(s2, sessions, &sessions) {
idx_ses++;
/*
* If we're just choosing windows, jump straight there. Note
* that this implies the current session, so only choose
* windows when the session matches this one.
*/
if (wflag && !sflag) {
if (s != s2)
continue;
goto windows_only;
}
wcd = window_choose_add_session(wl->window->active,
c, s2, ses_template, ses_action, idx_ses);
/* If we're just choosing sessions, skip choosing windows. */
if (sflag && !wflag) {
if (s == s2)
cur_win = idx_ses;
continue;
}
windows_only:
win_ses = win_max = -1;
RB_FOREACH(wm, winlinks, &s2->windows)
win_max++;
RB_FOREACH(wm, winlinks, &s2->windows) {
win_ses++;
if (sflag && wflag)
idx_ses++;
if (wm == s2->curw && s == s2) {
if (wflag && !sflag) {
/*
* Then we're only counting windows.
* So remember which is the current
* window in the list.
*/
cur_win = win_ses;
} else
cur_win = idx_ses;
}
xasprintf(&final_win_action, "%s %s %s",
wcd != NULL ? wcd->command : "",
wcd != NULL ? ";" : "", win_action);
if (win_ses != win_max)
cur_win_template = final_win_template_middle;
else
cur_win_template = final_win_template_last;
window_choose_add_window(wl->window->active,
c, s2, wm, cur_win_template,
final_win_action,
(wflag && !sflag) ? win_ses : idx_ses);
free(final_win_action);
}
/*
* If we're just drawing windows, don't consider moving on to
* other sessions as we only list windows in this session.
*/
if (wflag && !sflag)
break;
}
free(final_win_template_middle);
free(final_win_template_last);
window_choose_ready(wl->window->active, cur_win, NULL);
if (args_has(args, 'u')) {
window_choose_expand_all(wl->window->active);
window_choose_set_current(wl->window->active, cur_win);
}
mode = &window_tree_mode;
window_pane_set_mode(wp, mode, &item->target, args);
return (CMD_RETURN_NORMAL);
}

View File

@ -60,6 +60,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
struct client *c = item->client;
struct session *s;
struct window_pane *wp = item->target.wp;
int flag;
if (args_has(args, 'M')) {
if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL)
@ -69,12 +70,13 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
}
if (self->entry == &cmd_clock_mode_entry) {
window_pane_set_mode(wp, &window_clock_mode);
window_pane_set_mode(wp, &window_clock_mode, NULL, NULL);
return (CMD_RETURN_NORMAL);
}
if (wp->mode != &window_copy_mode) {
if (window_pane_set_mode(wp, &window_copy_mode) != 0)
flag = window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
if (flag != 0)
return (CMD_RETURN_NORMAL);
window_copy_init_from_pane(wp, args_has(self->args, 'e'));
}

View File

@ -18,9 +18,7 @@
#include <sys/types.h>
#include <fnmatch.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
@ -28,216 +26,69 @@
* Find window containing text.
*/
#define FIND_WINDOW_TEMPLATE \
"#{window_index}: #{window_name} " \
"[#{window_width}x#{window_height}] " \
"(#{window_panes} panes) #{window_find_matches}"
static enum cmd_retval cmd_find_window_exec(struct cmd *, struct cmdq_item *);
static void cmd_find_window_callback(struct window_choose_data *);
/* Flags for determining matching behavior. */
#define CMD_FIND_WINDOW_BY_TITLE 0x1
#define CMD_FIND_WINDOW_BY_CONTENT 0x2
#define CMD_FIND_WINDOW_BY_NAME 0x4
#define CMD_FIND_WINDOW_ALL \
(CMD_FIND_WINDOW_BY_TITLE | \
CMD_FIND_WINDOW_BY_CONTENT | \
CMD_FIND_WINDOW_BY_NAME)
const struct cmd_entry cmd_find_window_entry = {
.name = "find-window",
.alias = "findw",
.args = { "F:CNt:T", 1, 4 },
.usage = "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string",
.args = { "CNt:T", 1, 1 },
.usage = "[-CNT] " CMD_TARGET_PANE_USAGE " match-string",
.target = { 't', CMD_FIND_WINDOW, 0 },
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_find_window_exec
};
struct cmd_find_window_data {
struct winlink *wl;
char *list_ctx;
u_int pane_id;
TAILQ_ENTRY(cmd_find_window_data) entry;
};
TAILQ_HEAD(cmd_find_window_list, cmd_find_window_data);
static u_int cmd_find_window_match_flags(struct args *);
static void cmd_find_window_match(struct cmd_find_window_list *, int,
struct winlink *, const char *, const char *);
static u_int
cmd_find_window_match_flags(struct args *args)
{
u_int match_flags = 0;
/* Turn on flags based on the options. */
if (args_has(args, 'T'))
match_flags |= CMD_FIND_WINDOW_BY_TITLE;
if (args_has(args, 'C'))
match_flags |= CMD_FIND_WINDOW_BY_CONTENT;
if (args_has(args, 'N'))
match_flags |= CMD_FIND_WINDOW_BY_NAME;
/* If none of the flags were set, default to matching anything. */
if (match_flags == 0)
match_flags = CMD_FIND_WINDOW_ALL;
return (match_flags);
}
static void
cmd_find_window_match(struct cmd_find_window_list *find_list,
int match_flags, struct winlink *wl, const char *str,
const char *searchstr)
{
struct cmd_find_window_data *find_data;
struct window_pane *wp;
u_int i, line;
char *sres;
find_data = xcalloc(1, sizeof *find_data);
i = 0;
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
i++;
if ((match_flags & CMD_FIND_WINDOW_BY_NAME) &&
fnmatch(searchstr, wl->window->name, 0) == 0) {
find_data->list_ctx = xstrdup("");
break;
}
if ((match_flags & CMD_FIND_WINDOW_BY_TITLE) &&
fnmatch(searchstr, wp->base.title, 0) == 0) {
xasprintf(&find_data->list_ctx,
"pane %u title: \"%s\"", i - 1, wp->base.title);
break;
}
if (match_flags & CMD_FIND_WINDOW_BY_CONTENT &&
(sres = window_pane_search_old(wp, str, &line)) != NULL) {
xasprintf(&find_data->list_ctx,
"pane %u line %u: \"%s\"", i - 1, line + 1, sres);
free(sres);
break;
}
}
if (find_data->list_ctx != NULL) {
find_data->wl = wl;
find_data->pane_id = i - 1;
TAILQ_INSERT_TAIL(find_list, find_data, entry);
} else
free(find_data);
}
static enum cmd_retval
cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *current = &item->shared->current;
struct client *c = cmd_find_client(item, NULL, 1);
struct window_choose_data *cdata;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl, *wm;
struct cmd_find_window_list find_list;
struct cmd_find_window_data *find_data;
struct cmd_find_window_data *find_data1;
char *str, *searchstr;
const char *template;
u_int i, match_flags;
struct args *args = self->args, *new_args;
struct window_pane *wp = item->target.wp;
const char *s = args->argv[0];
char *filter, *argv = { NULL };
int C, N, T;
if (c == NULL) {
cmdq_error(item, "no client available");
return (CMD_RETURN_ERROR);
}
C = args_has(args, 'C');
N = args_has(args, 'N');
T = args_has(args, 'T');
if ((template = args_get(args, 'F')) == NULL)
template = FIND_WINDOW_TEMPLATE;
if (!C && !N && !T)
C = N = T = 1;
match_flags = cmd_find_window_match_flags(args);
str = args->argv[0];
if (C && N && T) {
xasprintf(&filter,
"#{||:"
"#{C:%s},#{||:#{m:*%s*,#{window_name}},"
"#{m:*%s*,#{pane_title}}}}",
s, s, s);
} else if (C && N) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{window_name}}}",
s, s);
} else if (C && T) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{pane_title}}}",
s, s);
} else if (N && T) {
xasprintf(&filter,
"#{||:#{m:*%s*,#{window_name}},#{m:*%s*,#{pane_title}}}",
s, s);
} else if (C)
xasprintf(&filter, "#{C:%s}", s);
else if (N)
xasprintf(&filter, "#{m:*%s*,#{window_name}}", s);
else if (T)
xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s);
TAILQ_INIT(&find_list);
new_args = args_parse("", 1, &argv);
args_set(new_args, 'f', filter);
xasprintf(&searchstr, "*%s*", str);
RB_FOREACH(wm, winlinks, &s->windows)
cmd_find_window_match(&find_list, match_flags, wm, str, searchstr);
free(searchstr);
window_pane_set_mode(wp, &window_tree_mode, &item->target, new_args);
if (TAILQ_EMPTY(&find_list)) {
cmdq_error(item, "no windows matching: %s", str);
return (CMD_RETURN_ERROR);
}
args_free(new_args);
free(filter);
if (TAILQ_NEXT(TAILQ_FIRST(&find_list), entry) == NULL) {
if (session_select(s, TAILQ_FIRST(&find_list)->wl->idx) == 0) {
cmd_find_from_session(current, s);
server_redraw_session(s);
}
recalculate_sizes();
goto out;
}
if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
goto out;
i = 0;
TAILQ_FOREACH(find_data, &find_list, entry) {
cdata = window_choose_data_create(TREE_OTHER, c, c->session);
cdata->idx = find_data->wl->idx;
cdata->wl = find_data->wl;
cdata->ft_template = xstrdup(template);
cdata->pane_id = find_data->pane_id;
format_add(cdata->ft, "line", "%u", i);
format_add(cdata->ft, "window_find_matches", "%s",
find_data->list_ctx);
format_defaults(cdata->ft, NULL, s, find_data->wl, NULL);
window_choose_add(wl->window->active, cdata);
i++;
}
window_choose_ready(wl->window->active, 0, cmd_find_window_callback);
out:
TAILQ_FOREACH_SAFE(find_data, &find_list, entry, find_data1) {
free(find_data->list_ctx);
TAILQ_REMOVE(&find_list, find_data, entry);
free(find_data);
}
return (CMD_RETURN_NORMAL);
}
static void
cmd_find_window_callback(struct window_choose_data *cdata)
{
struct session *s;
struct window_pane *wp;
if (cdata == NULL)
return;
s = cdata->start_session;
if (!session_alive(s))
return;
wp = window_pane_at_index(cdata->wl->window, cdata->pane_id);
if (wp != NULL && window_pane_visible(wp))
window_set_active_pane(cdata->wl->window, wp);
if (session_select(s, cdata->idx) == 0) {
server_redraw_session(s);
recalculate_sizes();
}
}

View File

@ -428,7 +428,8 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
w = c->session->curw->window;
if (w->active->mode != &window_copy_mode) {
window_pane_reset_mode(w->active);
window_pane_set_mode(w->active, &window_copy_mode);
window_pane_set_mode(w->active, &window_copy_mode, NULL,
NULL);
window_copy_init_for_output(w->active);
}
window_copy_vadd(w->active, fmt, ap);

View File

@ -75,7 +75,7 @@ cmd_run_shell_print(struct job *job, const char *msg)
return;
}
if (window_pane_set_mode(wp, &window_copy_mode) == 0)
if (window_pane_set_mode(wp, &window_copy_mode, NULL, NULL) == 0)
window_copy_init_for_output(wp);
if (wp->mode == &window_copy_mode)
window_copy_add(wp, "%s", msg);

View File

@ -134,7 +134,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
goto error;
}
new_wp = window_add_pane(w, wp, args_has(args, 'b'), hlimit);
layout_assign_pane(lc, new_wp);
layout_make_leaf(lc, new_wp);
path = NULL;
if (item->client != NULL && item->client->session == NULL)
@ -152,6 +152,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
}
environ_free(env);
layout_fix_panes(w, w->sx, w->sy);
server_redraw_window(w);
if (!args_has(args, 'd')) {

4
cmd.c
View File

@ -34,9 +34,7 @@ extern const struct cmd_entry cmd_break_pane_entry;
extern const struct cmd_entry cmd_capture_pane_entry;
extern const struct cmd_entry cmd_choose_buffer_entry;
extern const struct cmd_entry cmd_choose_client_entry;
extern const struct cmd_entry cmd_choose_session_entry;
extern const struct cmd_entry cmd_choose_tree_entry;
extern const struct cmd_entry cmd_choose_window_entry;
extern const struct cmd_entry cmd_clear_history_entry;
extern const struct cmd_entry cmd_clock_mode_entry;
extern const struct cmd_entry cmd_command_prompt_entry;
@ -123,9 +121,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_capture_pane_entry,
&cmd_choose_buffer_entry,
&cmd_choose_client_entry,
&cmd_choose_session_entry,
&cmd_choose_tree_entry,
&cmd_choose_window_entry,
&cmd_clear_history_entry,
&cmd_clock_mode_entry,
&cmd_command_prompt_entry,

View File

@ -19,11 +19,9 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <errno.h>
#include <fnmatch.h>
#include <libgen.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@ -840,7 +838,7 @@ format_choose(char *s, char **left, char **right)
}
/* Is this true? */
static int
int
format_true(const char *s)
{
if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))

View File

@ -196,9 +196,9 @@ key_bindings_init(void)
"bind p previous-window",
"bind q display-panes",
"bind r refresh-client",
"bind s choose-tree",
"bind s choose-tree -s",
"bind t clock-mode",
"bind w choose-window",
"bind w choose-tree -w",
"bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane",
"bind z resize-pane -Z",
"bind { swap-pane -U",

705
mode-tree.c Normal file
View File

@ -0,0 +1,705 @@
/* $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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
struct mode_tree_item;
TAILQ_HEAD(mode_tree_list, mode_tree_item);
struct mode_tree_data {
struct window_pane *wp;
void *modedata;
const char **sort_list;
u_int sort_size;
u_int sort_type;
void (*buildcb)(void *, u_int, uint64_t *);
struct screen *(*drawcb)(void *, void *, u_int, u_int);
struct mode_tree_list children;
struct mode_tree_list saved;
struct mode_tree_line *line_list;
u_int line_size;
u_int depth;
u_int width;
u_int height;
u_int offset;
u_int current;
struct screen screen;
};
struct mode_tree_item {
struct mode_tree_item *parent;
void *itemdata;
u_int line;
uint64_t tag;
const char *name;
const char *text;
int expanded;
int tagged;
struct mode_tree_list children;
TAILQ_ENTRY(mode_tree_item) entry;
};
struct mode_tree_line {
struct mode_tree_item *item;
u_int depth;
int last;
int flat;
};
static void mode_tree_free_items(struct mode_tree_list *);
static struct mode_tree_item *
mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
{
struct mode_tree_item *mti, *child;
TAILQ_FOREACH(mti, mtl, entry) {
if (mti->tag == tag)
return (mti);
child = mode_tree_find_item(&mti->children, tag);
if (child != NULL)
return (child);
}
return (NULL);
}
static void
mode_tree_free_item(struct mode_tree_item *mti)
{
mode_tree_free_items(&mti->children);
free((void *)mti->name);
free((void *)mti->text);
free(mti);
}
static void
mode_tree_free_items(struct mode_tree_list *mtl)
{
struct mode_tree_item *mti, *mti1;
TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) {
TAILQ_REMOVE(mtl, mti, entry);
mode_tree_free_item(mti);
}
}
static void
mode_tree_clear_lines(struct mode_tree_data *mtd)
{
free(mtd->line_list);
mtd->line_list = NULL;
mtd->line_size = 0;
}
static void
mode_tree_build_lines(struct mode_tree_data *mtd,
struct mode_tree_list *mtl, u_int depth)
{
struct mode_tree_item *mti;
struct mode_tree_line *line;
u_int i;
int flat = 1;
mtd->depth = depth;
TAILQ_FOREACH(mti, mtl, entry) {
mtd->line_list = xreallocarray(mtd->line_list,
mtd->line_size + 1, sizeof *mtd->line_list);
line = &mtd->line_list[mtd->line_size++];
line->item = mti;
line->depth = depth;
line->last = (mti == TAILQ_LAST(mtl, mode_tree_list));
mti->line = (mtd->line_size - 1);
if (!TAILQ_EMPTY(&mti->children))
flat = 0;
if (mti->expanded)
mode_tree_build_lines(mtd, &mti->children, depth + 1);
}
TAILQ_FOREACH(mti, mtl, entry) {
for (i = 0; i < mtd->line_size; i++) {
line = &mtd->line_list[i];
if (line->item == mti)
line->flat = flat;
}
}
}
static void
mode_tree_clear_tagged(struct mode_tree_list *mtl)
{
struct mode_tree_item *mti;
TAILQ_FOREACH(mti, mtl, entry) {
mti->tagged = 0;
mode_tree_clear_tagged(&mti->children);
}
}
void
mode_tree_up(struct mode_tree_data *mtd, int wrap)
{
if (mtd->current == 0) {
if (wrap) {
mtd->current = mtd->line_size - 1;
if (mtd->line_size >= mtd->height)
mtd->offset = mtd->line_size - mtd->height;
}
} else {
mtd->current--;
if (mtd->current < mtd->offset)
mtd->offset--;
}
}
void
mode_tree_down(struct mode_tree_data *mtd, int wrap)
{
if (mtd->current == mtd->line_size - 1) {
if (wrap) {
mtd->current = 0;
mtd->offset = 0;
}
} else {
mtd->current++;
if (mtd->current > mtd->offset + mtd->height - 1)
mtd->offset++;
}
}
void *
mode_tree_get_current(struct mode_tree_data *mtd)
{
return (mtd->line_list[mtd->current].item->itemdata);
}
u_int
mode_tree_count_tagged(struct mode_tree_data *mtd)
{
struct mode_tree_item *mti;
u_int i, tagged;
tagged = 0;
for (i = 0; i < mtd->line_size; i++) {
mti = mtd->line_list[i].item;
if (mti->tagged)
tagged++;
}
return (tagged);
}
void
mode_tree_each_tagged(struct mode_tree_data *mtd, void (*cb)(void *, void *,
key_code), key_code key, int current)
{
struct mode_tree_item *mti;
u_int i;
int fired;
fired = 0;
for (i = 0; i < mtd->line_size; i++) {
mti = mtd->line_list[i].item;
if (mti->tagged) {
fired = 1;
cb(mtd->modedata, mti->itemdata, key);
}
}
if (!fired && current) {
mti = mtd->line_list[mtd->current].item;
cb(mtd->modedata, mti->itemdata, key);
}
}
struct mode_tree_data *
mode_tree_start(struct window_pane *wp, void (*buildcb)(void *, u_int,
uint64_t *), struct screen *(*drawcb)(void *, void *, u_int, u_int),
void *modedata, const char **sort_list, u_int sort_size, struct screen **s)
{
struct mode_tree_data *mtd;
mtd = xcalloc(1, sizeof *mtd);
mtd->wp = wp;
mtd->modedata = modedata;
mtd->sort_list = sort_list;
mtd->sort_size = sort_size;
mtd->sort_type = 0;
mtd->buildcb = buildcb;
mtd->drawcb = drawcb;
TAILQ_INIT(&mtd->children);
*s = &mtd->screen;
screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
(*s)->mode &= ~MODE_CURSOR;
return (mtd);
}
void
mode_tree_build(struct mode_tree_data *mtd)
{
struct screen *s = &mtd->screen;
uint64_t tag;
u_int i;
if (mtd->line_list != NULL)
tag = mtd->line_list[mtd->current].item->tag;
else
tag = 0;
TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
TAILQ_INIT(&mtd->children);
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag);
mode_tree_free_items(&mtd->saved);
TAILQ_INIT(&mtd->saved);
mode_tree_clear_lines(mtd);
mode_tree_build_lines(mtd, &mtd->children, 0);
for (i = 0; i < mtd->line_size; i++) {
if (mtd->line_list[i].item->tag == tag)
break;
}
if (i != mtd->line_size)
mtd->current = i;
else {
mtd->current = 0;
mtd->offset = 0;
}
mtd->width = screen_size_x(s);
mtd->height = (screen_size_y(s) / 3) * 2;
if (mtd->height > mtd->line_size)
mtd->height = screen_size_y(s) / 2;
if (mtd->height < 10)
mtd->height = screen_size_y(s);
if (screen_size_y(s) - mtd->height < 2)
mtd->height = screen_size_y(s);
}
void
mode_tree_free(struct mode_tree_data *mtd)
{
mode_tree_free_items(&mtd->children);
mode_tree_clear_lines(mtd);
screen_free(&mtd->screen);
free(mtd);
}
void
mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
{
struct screen *s = &mtd->screen;
screen_resize(s, sx, sy, 0);
mode_tree_build(mtd);
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
}
struct mode_tree_item *
mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
void *itemdata, uint64_t tag, const char *name, const char *text,
int expanded)
{
struct mode_tree_item *mti, *saved;
log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
name, text);
mti = xcalloc(1, sizeof *mti);
mti->parent = parent;
mti->itemdata = itemdata;
mti->tag = tag;
mti->name = xstrdup(name);
mti->text = xstrdup(text);
saved = mode_tree_find_item(&mtd->saved, tag);
if (saved != NULL) {
if (parent == NULL || (parent != NULL && parent->expanded))
mti->tagged = saved->tagged;
mti->expanded = saved->expanded;
} else if (expanded == -1)
mti->expanded = 1;
else
mti->expanded = expanded;
TAILQ_INIT(&mti->children);
if (parent != NULL)
TAILQ_INSERT_TAIL(&parent->children, mti, entry);
else
TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
return (mti);
}
void
mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
{
struct mode_tree_item *parent = mti->parent;
if (parent != NULL)
TAILQ_REMOVE(&parent->children, mti, entry);
else
TAILQ_REMOVE(&mtd->children, mti, entry);
mode_tree_free_item(mti);
}
void
mode_tree_draw(struct mode_tree_data *mtd)
{
struct window_pane *wp = mtd->wp;
struct screen *s = &mtd->screen, *box;
struct mode_tree_line *line;
struct mode_tree_item *mti;
struct options *oo = wp->window->options;
struct screen_write_ctx ctx;
struct grid_cell gc0, gc;
u_int w, h, i, j, sy, box_x, box_y;
char *text, *start, key[7];
const char *tag, *symbol;
size_t size;
int keylen;
if (mtd->line_size == 0)
return;
memcpy(&gc0, &grid_default_cell, sizeof gc0);
memcpy(&gc, &grid_default_cell, sizeof gc);
style_apply(&gc, oo, "mode-style");
w = mtd->width;
h = mtd->height;
screen_write_start(&ctx, NULL, s);
screen_write_clearscreen(&ctx, 8);
if (mtd->line_size > 10)
keylen = 6;
else
keylen = 4;
for (i = 0; i < mtd->line_size; i++) {
if (i < mtd->offset)
continue;
if (i > mtd->offset + h - 1)
break;
line = &mtd->line_list[i];
mti = line->item;
screen_write_cursormove(&ctx, 0, i - mtd->offset);
if (i < 10)
snprintf(key, sizeof key, "(%c)", '0' + i);
else if (i < 36)
snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
else
*key = '\0';
if (line->flat)
symbol = "";
else if (TAILQ_EMPTY(&mti->children))
symbol = " ";
else if (mti->expanded)
symbol = "- ";
else
symbol = "+ ";
if (line->depth == 0)
start = xstrdup(symbol);
else {
size = (4 * line->depth) + 32;
start = xcalloc(1, size);
for (j = 1; j < line->depth; j++) {
if (mti->parent != NULL &&
mtd->line_list[mti->parent->line].last)
strlcat(start, " ", size);
else
strlcat(start, "\001x\001 ", size);
}
if (line->last)
strlcat(start, "\001mq\001> ", size);
else
strlcat(start, "\001tq\001> ", size);
strlcat(start, symbol, size);
}
if (mti->tagged)
tag = "*";
else
tag = "";
xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start,
mti->name, tag, mti->text);
free(start);
if (mti->tagged) {
gc.attr ^= GRID_ATTR_BRIGHT;
gc0.attr ^= GRID_ATTR_BRIGHT;
}
if (i != mtd->current) {
screen_write_puts(&ctx, &gc0, "%.*s", w, text);
screen_write_clearendofline(&ctx, 8);
} else
screen_write_puts(&ctx, &gc, "%-*.*s", w, w, text);
free(text);
if (mti->tagged) {
gc.attr ^= GRID_ATTR_BRIGHT;
gc0.attr ^= GRID_ATTR_BRIGHT;
}
}
sy = screen_size_y(s);
if (sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
screen_write_stop(&ctx);
return;
}
line = &mtd->line_list[mtd->current];
mti = line->item;
screen_write_cursormove(&ctx, 0, h);
screen_write_box(&ctx, w, sy - h);
xasprintf(&text, " %s (sort: %s) ", mti->name,
mtd->sort_list[mtd->sort_type]);
if (w - 2 >= strlen(text)) {
screen_write_cursormove(&ctx, 1, h);
screen_write_puts(&ctx, &gc0, "%s", text);
}
free(text);
box_x = w - 4;
box_y = sy - h - 2;
box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y);
if (box != NULL) {
screen_write_cursormove(&ctx, 2, h + 1);
screen_write_copy(&ctx, box, 0, 0, box_x, box_y, NULL, NULL);
screen_free(box);
}
screen_write_stop(&ctx);
}
int
mode_tree_key(struct mode_tree_data *mtd, key_code *key, struct mouse_event *m)
{
struct mode_tree_line *line;
struct mode_tree_item *current, *parent;
u_int i, x, y;
int choice;
key_code tmp;
if (*key == KEYC_MOUSEDOWN1_PANE) {
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
*key = KEYC_NONE;
return (0);
}
if (x > mtd->width || y > mtd->height) {
*key = KEYC_NONE;
return (0);
}
if (mtd->offset + y < mtd->line_size) {
mtd->current = mtd->offset + y;
*key = '\r';
return (0);
}
}
line = &mtd->line_list[mtd->current];
current = line->item;
choice = -1;
if (*key >= '0' && *key <= '9')
choice = (*key) - '0';
else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
tmp = (*key) & KEYC_MASK_KEY;
if (tmp >= 'a' && tmp <= 'z')
choice = 10 + (tmp - 'a');
}
if (choice != -1) {
if ((u_int)choice > mtd->line_size - 1) {
*key = KEYC_NONE;
return (0);
}
mtd->current = choice;
*key = '\r';
return (0);
}
switch (*key) {
case 'q':
case '\033': /* Escape */
return (1);
case KEYC_UP:
case 'k':
case KEYC_WHEELUP_PANE:
mode_tree_up(mtd, 1);
break;
case KEYC_DOWN:
case 'j':
case KEYC_WHEELDOWN_PANE:
mode_tree_down(mtd, 1);
break;
case KEYC_PPAGE:
case '\002': /* C-b */
for (i = 0; i < mtd->height; i++) {
if (mtd->current == 0)
break;
mode_tree_up(mtd, 1);
}
break;
case KEYC_NPAGE:
case '\006': /* C-f */
for (i = 0; i < mtd->height; i++) {
if (mtd->current == mtd->line_size - 1)
break;
mode_tree_down(mtd, 1);
}
break;
case KEYC_HOME:
mtd->current = 0;
mtd->offset = 0;
break;
case KEYC_END:
mtd->current = mtd->line_size - 1;
if (mtd->current > mtd->height - 1)
mtd->offset = mtd->current - mtd->height;
else
mtd->offset = 0;
break;
case 't':
/*
* Do not allow parents and children to both be tagged: untag
* all parents and children of current.
*/
if (!current->tagged) {
parent = current->parent;
while (parent != NULL) {
parent->tagged = 0;
parent = parent->parent;
}
mode_tree_clear_tagged(&current->children);
current->tagged = 1;
} else
current->tagged = 0;
mode_tree_down(mtd, 0);
break;
case 'T':
for (i = 0; i < mtd->line_size; i++)
mtd->line_list[i].item->tagged = 0;
break;
case '\024': /* C-t */
for (i = 0; i < mtd->line_size; i++) {
if (mtd->line_list[i].item->parent == NULL)
mtd->line_list[i].item->tagged = 1;
else
mtd->line_list[i].item->tagged = 0;
}
break;
case 'O':
mtd->sort_type++;
if (mtd->sort_type == mtd->sort_size)
mtd->sort_type = 0;
mode_tree_build(mtd);
break;
case KEYC_LEFT:
case '-':
if (line->flat || !current->expanded)
current = current->parent;
if (current == NULL)
mode_tree_up(mtd, 0);
else {
current->expanded = 0;
mtd->current = current->line;
mode_tree_build(mtd);
}
break;
case KEYC_RIGHT:
case '+':
if (line->flat || current->expanded)
mode_tree_down(mtd, 0);
else if (!line->flat) {
current->expanded = 1;
mode_tree_build(mtd);
}
break;
}
return (0);
}
void
mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
const char *template, const char *name)
{
struct cmdq_item *new_item;
struct cmd_list *cmdlist;
char *command, *cause;
command = cmd_template_replace(template, name, 1);
if (command == NULL || *command == '\0')
return;
cmdlist = cmd_string_parse(command, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL && c != NULL) {
*cause = toupper((u_char)*cause);
status_message_set(c, "%s", cause);
}
free(cause);
} else {
new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
cmdq_append(c, new_item);
cmd_list_free(cmdlist);
}
free(command);
}

View File

@ -62,7 +62,7 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.minimum = 1,
.maximum = INT_MAX,
.default_num = 20
.default_num = 50
},
{ .name = "command-alias",
@ -71,7 +71,9 @@ const struct options_table_entry options_table[] = {
.default_str = "split-pane=split-window,"
"splitp=split-window,"
"server-info=show-messages -JT,"
"info=show-messages -JT",
"info=show-messages -JT,"
"choose-window=choose-tree -w,"
"choose-session=choose-tree -s",
.separator = ","
},

View File

@ -362,6 +362,9 @@ screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px,
struct grid_cell gc;
u_int xx, yy, cx, cy, b;
if (nx == 0 || ny == 0)
return;
cx = s->cx;
cy = s->cy;
@ -384,6 +387,121 @@ screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px,
}
}
/* Draw a line on screen. */
void
screen_write_line(struct screen_write_ctx *ctx, u_int nx, int left, int right)
{
struct screen *s = ctx->s;
struct grid_cell gc;
u_int cx, cy, i;
cx = s->cx;
cy = s->cy;
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= GRID_ATTR_CHARSET;
screen_write_putc(ctx, &gc, left ? 't' : 'q');
for (i = 1; i < nx - 1; i++)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, right ? 'u' : 'q');
screen_write_cursormove(ctx, cx, cy);
}
/* Draw a box on screen. */
void
screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny)
{
struct screen *s = ctx->s;
struct grid_cell gc;
u_int cx, cy, i;
cx = s->cx;
cy = s->cy;
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= GRID_ATTR_CHARSET;
screen_write_putc(ctx, &gc, 'l');
for (i = 1; i < nx - 1; i++)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, 'k');
screen_write_cursormove(ctx, cx, cy + ny - 1);
screen_write_putc(ctx, &gc, 'm');
for (i = 1; i < nx - 1; i++)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, 'j');
for (i = 1; i < ny - 1; i++) {
screen_write_cursormove(ctx, cx, cy + i);
screen_write_putc(ctx, &gc, 'x');
}
for (i = 1; i < ny - 1; i++) {
screen_write_cursormove(ctx, cx + nx - 1, cy + i);
screen_write_putc(ctx, &gc, 'x');
}
screen_write_cursormove(ctx, cx, cy);
}
/* Write a preview version of a window. */
void
screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
u_int ny)
{
struct screen *s = ctx->s;
struct grid_cell gc;
u_int cx, cy, px, py;
cx = s->cx;
cy = s->cy;
/*
* If the cursor is on, pick the area around the cursor, otherwise use
* the top left.
*/
if (src->mode & MODE_CURSOR) {
px = src->cx;
if (px < nx / 3)
px = 0;
else
px = px - nx / 3;
if (px + nx > screen_size_x(src)) {
if (nx > screen_size_x(src))
px = 0;
else
px = screen_size_x(src) - nx;
}
py = src->cy;
if (py < ny / 3)
py = 0;
else
py = py - ny / 3;
if (py + ny > screen_size_y(src)) {
if (ny > screen_size_y(src))
py = 0;
else
py = screen_size_y(src) - ny;
}
} else {
px = 0;
py = 0;
}
screen_write_copy(ctx, src, px, src->grid->hsize + py, nx, ny, NULL,
NULL);
if (src->mode & MODE_CURSOR) {
grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
gc.attr |= GRID_ATTR_REVERSE;
screen_write_cursormove(ctx, cx + (src->cx - px),
cy + (src->cy - py));
screen_write_cell(ctx, &gc);
}
}
/* Set up context for TTY command. */
static void
screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)

199
tmux.1
View File

@ -1339,136 +1339,76 @@ the end of the visible pane.
The default is to capture only the visible contents of the pane.
.It Xo
.Ic choose-client
.Op Fl F Ar format
.Op Fl t Ar target-window
.Op Fl t Ar target-pane
.Op Ar template
.Xc
Put a window into client choice mode, allowing a client to be selected
interactively from a list.
Put a pane into client mode, allowing a client to be selected interactively from
a list.
The following keys may be used in client mode:
.Bl -column "Key" "Function" -offset indent
.It Sy "Key" Ta Sy "Function"
.It Li "Enter" Ta "Choose selected client"
.It Li "Up" Ta "Select previous client"
.It Li "Down" Ta "Select next client"
.It Li "t" Ta "Toggle if client is tagged"
.It Li "T" Ta "Tag no clients"
.It Li "C-t" Ta "Tag all clients"
.It Li "d" Ta "Detach selected client"
.It Li "D" Ta "Detach tagged clients"
.It Li "x" Ta "Detach and HUP selected client"
.It Li "X" Ta "Detach and HUP tagged clients"
.It Li "z" Ta "Suspend selected client"
.It Li "Z" Ta "Suspend tagged clients"
.It Li "O" Ta "Change sort order"
.It Li "q" Ta "Exit mode"
.El
.Pp
After a client is chosen,
.Ql %%
is replaced by the client
.Xr pty 4
path in
is replaced by the client name in
.Ar template
and the result executed as a command.
If
.Ar template
is not given, "detach-client -t '%%'" is used.
For the meaning of the
.Fl F
flag, see the
.Sx FORMATS
section.
.Pp
This command works only if at least one client is attached.
.It Xo
.Ic choose-session
.Op Fl F Ar format
.Op Fl t Ar target-window
.Ic choose-tree
.Op Fl sw
.Op Fl t Ar target-pane
.Op Ar template
.Xc
Put a window into session choice mode, where a session may be selected
Put a pane into tree mode, where a session, window or pane may be chosen
interactively from a list.
When one is chosen,
.Fl s
starts with sessions collapsed and
.Fl w
with windows collapsed.
The following keys may be used in tree mode:
.Bl -column "Key" "Function" -offset indent
.It Sy "Key" Ta Sy "Function"
.It Li "Enter" Ta "Choose selected item"
.It Li "Up" Ta "Select previous item"
.It Li "Down" Ta "Select next item"
.It Li "t" Ta "Toggle if item is tagged"
.It Li "T" Ta "Tag no items"
.It Li "C-t" Ta "Tag all items"
.It Li ":" Ta "Run a command for each tagged item"
.It Li "f" Ta "Enter a format to filter items"
.It Li "O" Ta "Change sort order"
.It Li "q" Ta "Exit mode"
.El
.Pp
After a session, window or pane is chosen,
.Ql %%
is replaced by the session name in
is replaced by the target in
.Ar template
and the result executed as a command.
If
.Ar template
is not given, "switch-client -t '%%'" is used.
For the meaning of the
.Fl F
flag, see the
.Sx FORMATS
section.
This command works only if at least one client is attached.
.It Xo
.Ic choose-tree
.Op Fl suw
.Op Fl b Ar session-template
.Op Fl c Ar window-template
.Op Fl S Ar format
.Op Fl W Ar format
.Op Fl t Ar target-window
.Xc
Put a window into tree choice mode, where either sessions or windows may be
selected interactively from a list.
By default, windows belonging to a session are indented to show their
relationship to a session.
.Pp
Note that the
.Ic choose-window
and
.Ic choose-session
commands are wrappers around
.Ic choose-tree .
.Pp
If
.Fl s
is given, will show sessions.
If
.Fl w
is given, will show windows.
.Pp
By default, the tree is collapsed and sessions must be expanded to windows
with the right arrow key.
The
.Fl u
option will start with all sessions expanded instead.
.Pp
If
.Fl b
is given, will override the default session command.
Note that
.Ql %%
can be used and will be replaced with the session name.
The default option if not specified is "switch-client -t '%%'".
If
.Fl c
is given, will override the default window command.
Like
.Fl b ,
.Ql %%
can be used and will be replaced with the session name and window index.
When a window is chosen from the list, the session command is run before the
window command.
.Pp
.Fl S
uses
.Ar format
instead of the default session
format and
.Fl W
instead of the default window format.
For the meaning of
.Ar format ,
see the
.Sx FORMATS
section.
.Pp
This command works only if at least one client is attached.
.It Xo
.Ic choose-window
.Op Fl F Ar format
.Op Fl t Ar target-window
.Op Ar template
.Xc
Put a window into window choice mode, where a window may be chosen
interactively from a list.
After a window is selected,
.Ql %%
is replaced by the session name and window index in
.Ar template
and the result executed as a command.
If
.Ar template
is not given, "select-window -t '%%'" is used.
For the meaning of the
.Fl F
flag, see the
.Sx FORMATS
section.
This command works only if at least one client is attached.
.It Xo
.Ic display-panes
@ -1498,8 +1438,7 @@ The default
is "select-pane -t '%%'".
.It Xo Ic find-window
.Op Fl CNT
.Op Fl F Ar format
.Op Fl t Ar target-window
.Op Fl t Ar target-pane
.Ar match-string
.Xc
.D1 (alias: Ic findw )
@ -1517,13 +1456,7 @@ matches only the window name and
matches only the window title.
The default is
.Fl CNT .
If only one window is matched, it'll be automatically selected,
otherwise a choice list is shown.
For the meaning of the
.Fl F
flag, see the
.Sx FORMATS
section.
.Pp
This command works only if at least one client is attached.
.It Xo Ic join-pane
.Op Fl bdhv
@ -3982,13 +3915,27 @@ The buffer commands are as follows:
.Bl -tag -width Ds
.It Xo
.Ic choose-buffer
.Op Fl F Ar format
.Op Fl t Ar target-window
.Op Fl t Ar target-pane
.Op Ar template
.Xc
Put a window into buffer choice mode, where a buffer may be chosen
interactively from a list.
After a buffer is selected,
Put a pane into buffer mode, where a buffer may be chosen interactively from
a list.
The following keys may be used in buffer mode:
.Bl -column "Key" "Function" -offset indent
.It Sy "Key" Ta Sy "Function"
.It Li "Enter" Ta "Choose selected buffer"
.It Li "Up" Ta "Select previous buffer"
.It Li "Down" Ta "Select next buffer"
.It Li "t" Ta "Toggle if buffer is tagged"
.It Li "T" Ta "Tag no buffers"
.It Li "C-t" Ta "Tag all buffers"
.It Li "d" Ta "Delete selected buffer"
.It Li "D" Ta "Delete tagged buffers"
.It Li "O" Ta "Change sort order"
.It Li "q" Ta "Exit mode"
.El
.Pp
After a buffer is chosen,
.Ql %%
is replaced by the buffer name in
.Ar template
@ -3996,11 +3943,7 @@ and the result executed as a command.
If
.Ar template
is not given, "paste-buffer -b '%%'" is used.
For the meaning of the
.Fl F
flag, see the
.Sx FORMATS
section.
.Pp
This command works only if at least one client is attached.
.It Ic clear-history Op Fl t Ar target-pane
.D1 (alias: Ic clearhist )

69
tmux.h
View File

@ -40,12 +40,13 @@ extern char **environ;
struct args;
struct client;
struct cmd_find_state;
struct cmdq_item;
struct cmdq_list;
struct environ;
struct format_job_tree;
struct input_ctx;
struct mode_key_cmdstr;
struct mode_tree_data;
struct mouse_event;
struct options;
struct options_entry;
@ -693,7 +694,8 @@ struct screen_write_ctx {
struct window_mode {
const char *name;
struct screen *(*init)(struct window_pane *);
struct screen *(*init)(struct window_pane *, struct cmd_find_state *,
struct args *);
void (*free)(struct window_pane *);
void (*resize)(struct window_pane *, u_int, u_int);
void (*key)(struct window_pane *, struct client *,
@ -1518,6 +1520,7 @@ char *paste_make_sample(struct paste_buffer *);
#define FORMAT_PANE 0x80000000U
#define FORMAT_WINDOW 0x40000000U
struct format_tree;
int format_true(const char *);
struct format_tree *format_create(struct client *, struct cmdq_item *, int,
int);
void format_free(struct format_tree *);
@ -1717,6 +1720,7 @@ void tty_keys_free(struct tty *);
key_code tty_keys_next(struct tty *);
/* arguments.c */
void args_set(struct args *, u_char, const char *);
struct args *args_parse(const char *, int, char **);
void args_free(struct args *);
char *args_print(struct args *);
@ -1997,6 +2001,10 @@ void screen_write_putc(struct screen_write_ctx *, const struct grid_cell *,
u_char);
void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int,
u_int, u_int, u_int, bitstr_t *, const struct grid_cell *);
void screen_write_line(struct screen_write_ctx *, u_int, int, int);
void screen_write_box(struct screen_write_ctx *, u_int, u_int);
void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int,
u_int);
void screen_write_backspace(struct screen_write_ctx *);
void screen_write_mode_set(struct screen_write_ctx *, int);
void screen_write_mode_clear(struct screen_write_ctx *, int);
@ -2119,15 +2127,15 @@ void window_pane_unset_palette(struct window_pane *, u_int);
void window_pane_reset_palette(struct window_pane *);
int window_pane_get_palette(const struct window_pane *, int);
int window_pane_set_mode(struct window_pane *,
const struct window_mode *);
const struct window_mode *, struct cmd_find_state *,
struct args *);
void window_pane_reset_mode(struct window_pane *);
void window_pane_key(struct window_pane *, struct client *,
struct session *, key_code, struct mouse_event *);
int window_pane_outside(struct window_pane *);
int window_pane_visible(struct window_pane *);
u_int window_pane_search(struct window_pane *, const char *);
char *window_pane_search_old(struct window_pane *, const char *,
u_int *);
const char *window_printable_flags(struct winlink *);
struct window_pane *window_pane_find_up(struct window_pane *);
struct window_pane *window_pane_find_down(struct window_pane *);
@ -2176,10 +2184,43 @@ u_int layout_set_select(struct window *, u_int);
u_int layout_set_next(struct window *);
u_int layout_set_previous(struct window *);
/* mode-tree.c */
u_int mode_tree_count_tagged(struct mode_tree_data *);
void *mode_tree_get_current(struct mode_tree_data *);
void mode_tree_each_tagged(struct mode_tree_data *, void (*)(void *, void *,
key_code), key_code, int);
void mode_tree_up(struct mode_tree_data *, int);
void mode_tree_down(struct mode_tree_data *, int);
struct mode_tree_data *mode_tree_start(struct window_pane *,
void (*)(void *, u_int, uint64_t *), struct screen *(*)(void *,
void *, u_int, u_int), void *, const char **, u_int,
struct screen **);
void mode_tree_build(struct mode_tree_data *);
void mode_tree_free(struct mode_tree_data *);
void mode_tree_resize(struct mode_tree_data *, u_int, u_int);
struct mode_tree_item *mode_tree_add(struct mode_tree_data *,
struct mode_tree_item *, void *, uint64_t, const char *,
const char *, int);
void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *);
void mode_tree_draw(struct mode_tree_data *);
int mode_tree_key(struct mode_tree_data *, key_code *,
struct mouse_event *);
void mode_tree_run_command(struct client *, struct cmd_find_state *,
const char *, const char *);
/* window-buffer.c */
extern const struct window_mode window_buffer_mode;
/* window-tree.c */
extern const struct window_mode window_tree_mode;
/* window-clock.c */
extern const struct window_mode window_clock_mode;
extern const char window_clock_table[14][5][5];
/* window-client.c */
extern const struct window_mode window_client_mode;
/* window-copy.c */
extern const struct window_mode window_copy_mode;
void window_copy_init_from_pane(struct window_pane *, int);
@ -2190,24 +2231,6 @@ void window_copy_pageup(struct window_pane *, int);
void window_copy_start_drag(struct client *, struct mouse_event *);
int window_copy_scroll_position(struct window_pane *);
/* window-choose.c */
extern const struct window_mode window_choose_mode;
void window_choose_add(struct window_pane *,
struct window_choose_data *);
void window_choose_ready(struct window_pane *,
u_int, void (*)(struct window_choose_data *));
struct window_choose_data *window_choose_data_create (int,
struct client *, struct session *);
void window_choose_data_run(struct window_choose_data *);
struct window_choose_data *window_choose_add_window(struct window_pane *,
struct client *, struct session *, struct winlink *,
const char *, const char *, u_int);
struct window_choose_data *window_choose_add_session(struct window_pane *,
struct client *, struct session *, const char *,
const char *, u_int);
void window_choose_expand_all(struct window_pane *);
void window_choose_set_current(struct window_pane *, u_int);
/* names.c */
void check_window_name(struct window *);
char *default_window_name(struct window *);

342
window-buffer.c Normal file
View File

@ -0,0 +1,342 @@
/* $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 <stdlib.h>
#include <string.h>
#include <vis.h>
#include "tmux.h"
static struct screen *window_buffer_init(struct window_pane *,
struct cmd_find_state *, struct args *);
static void window_buffer_free(struct window_pane *);
static void window_buffer_resize(struct window_pane *, u_int,
u_int);
static void window_buffer_key(struct window_pane *,
struct client *, struct session *, key_code,
struct mouse_event *);
#define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -b '%%'"
const struct window_mode window_buffer_mode = {
.name = "buffer-mode",
.init = window_buffer_init,
.free = window_buffer_free,
.resize = window_buffer_resize,
.key = window_buffer_key,
};
enum window_buffer_sort_type {
WINDOW_BUFFER_BY_NAME,
WINDOW_BUFFER_BY_TIME,
WINDOW_BUFFER_BY_SIZE,
};
static const char *window_buffer_sort_list[] = {
"name",
"time",
"size"
};
struct window_buffer_itemdata {
const char *name;
time_t created;
u_int order;
size_t size;
};
struct window_buffer_modedata {
struct mode_tree_data *data;
char *command;
struct window_buffer_itemdata **item_list;
u_int item_size;
};
static struct window_buffer_itemdata *
window_buffer_add_item(struct window_buffer_modedata *data)
{
struct window_buffer_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_buffer_free_item(struct window_buffer_itemdata *item)
{
free((void *)item->name);
free(item);
}
static int
window_buffer_cmp_name(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
return (strcmp((*a)->name, (*b)->name));
}
static int
window_buffer_cmp_time(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
if ((*a)->order > (*b)->order)
return (-1);
if ((*a)->order < (*b)->order)
return (1);
return (strcmp((*a)->name, (*b)->name));
}
static int
window_buffer_cmp_size(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
if ((*a)->size > (*b)->size)
return (-1);
if ((*a)->size < (*b)->size)
return (1);
return (strcmp((*a)->name, (*b)->name));
}
static void
window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag)
{
struct window_buffer_modedata *data = modedata;
struct window_buffer_itemdata *item;
u_int i;
struct paste_buffer *pb;
char *tim;
char *text;
for (i = 0; i < data->item_size; i++)
window_buffer_free_item(data->item_list[i]);
free(data->item_list);
data->item_list = NULL;
data->item_size = 0;
pb = NULL;
while ((pb = paste_walk(pb)) != NULL) {
item = window_buffer_add_item(data);
item->name = xstrdup(paste_buffer_name(pb));
item->created = paste_buffer_created(pb);
paste_buffer_data(pb, &item->size);
item->order = paste_buffer_order(pb);
}
switch (sort_type) {
case WINDOW_BUFFER_BY_NAME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_name);
break;
case WINDOW_BUFFER_BY_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_time);
break;
case WINDOW_BUFFER_BY_SIZE:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_size);
break;
}
for (i = 0; i < data->item_size; i++) {
item = data->item_list[i];
tim = ctime(&item->created);
*strchr(tim, '\n') = '\0';
xasprintf(&text, "%zu bytes (%s)", item->size, tim);
mode_tree_add(data->data, NULL, item, item->order, item->name,
text, -1);
free(text);
}
}
static struct screen *
window_buffer_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
{
struct window_buffer_itemdata *item = itemdata;
struct paste_buffer *pb;
static struct screen s;
struct screen_write_ctx ctx;
char line[1024];
const char *pdata, *end, *cp;
size_t psize, at;
u_int i;
pb = paste_get_name(item->name);
if (pb == NULL)
return (NULL);
screen_init(&s, sx, sy, 0);
screen_write_start(&ctx, NULL, &s);
screen_write_clearscreen(&ctx, 8);
pdata = end = paste_buffer_data (pb, &psize);
for (i = 0; i < sy; i++) {
at = 0;
while (end != pdata + psize && *end != '\n') {
if ((sizeof line) - at > 5) {
cp = vis(line + at, *end, VIS_TAB|VIS_OCTAL, 0);
at = cp - line;
}
end++;
}
if (at > sx)
at = sx;
line[at] = '\0';
if (*line != '\0') {
screen_write_cursormove(&ctx, 0, i);
screen_write_puts(&ctx, &grid_default_cell, "%s", line);
}
if (end == pdata + psize)
break;
end++;
}
screen_write_stop(&ctx);
return (&s);
}
static struct screen *
window_buffer_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
struct args *args)
{
struct window_buffer_modedata *data;
struct screen *s;
wp->modedata = data = xcalloc(1, sizeof *data);
if (args == NULL || args->argc == 0)
data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND);
else
data->command = xstrdup(args->argv[0]);
data->data = mode_tree_start(wp, window_buffer_build,
window_buffer_draw, data, window_buffer_sort_list,
nitems(window_buffer_sort_list), &s);
mode_tree_build(data->data);
mode_tree_draw(data->data);
return (s);
}
static void
window_buffer_free(struct window_pane *wp)
{
struct window_buffer_modedata *data = wp->modedata;
u_int i;
if (data == NULL)
return;
mode_tree_free(data->data);
for (i = 0; i < data->item_size; i++)
window_buffer_free_item(data->item_list[i]);
free(data->item_list);
free(data->command);
free(data);
}
static void
window_buffer_resize(struct window_pane *wp, u_int sx, u_int sy)
{
struct window_buffer_modedata *data = wp->modedata;
mode_tree_resize(data->data, sx, sy);
}
static void
window_buffer_do_delete(void* modedata, void *itemdata, __unused key_code key)
{
struct window_buffer_modedata *data = modedata;
struct window_buffer_itemdata *item = itemdata;
struct paste_buffer *pb;
if (item == mode_tree_get_current(data->data))
mode_tree_down(data->data, 0);
if ((pb = paste_get_name(item->name)) != NULL)
paste_free(pb);
}
static void
window_buffer_key(struct window_pane *wp, struct client *c,
__unused struct session *s, key_code key, struct mouse_event *m)
{
struct window_buffer_modedata *data = wp->modedata;
struct window_buffer_itemdata *item;
char *command, *name;
int finished;
/*
* t = toggle tag
* T = tag none
* C-t = tag all
* q = exit
* O = change sort order
*
* d = delete buffer
* D = delete tagged buffers
* Enter = paste buffer
*/
finished = mode_tree_key(data->data, &key, m);
switch (key) {
case 'd':
item = mode_tree_get_current(data->data);
window_buffer_do_delete(data, item, key);
mode_tree_build(data->data);
break;
case 'D':
mode_tree_each_tagged(data->data, window_buffer_do_delete, key,
0);
mode_tree_build(data->data);
break;
case '\r':
item = mode_tree_get_current(data->data);
command = xstrdup(data->command);
name = xstrdup(item->name);
window_pane_reset_mode(wp);
mode_tree_run_command(c, NULL, command, name);
free(name);
free(command);
return;
}
if (finished || paste_get_top(NULL) == NULL)
window_pane_reset_mode(wp);
else {
mode_tree_draw(data->data);
wp->flags |= PANE_REDRAW;
}
}

File diff suppressed because it is too large Load Diff

335
window-client.c Normal file
View File

@ -0,0 +1,335 @@
/* $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 <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
static struct screen *window_client_init(struct window_pane *,
struct cmd_find_state *, struct args *);
static void window_client_free(struct window_pane *);
static void window_client_resize(struct window_pane *, u_int,
u_int);
static void window_client_key(struct window_pane *,
struct client *, struct session *, key_code,
struct mouse_event *);
#define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'"
const struct window_mode window_client_mode = {
.name = "client-mode",
.init = window_client_init,
.free = window_client_free,
.resize = window_client_resize,
.key = window_client_key,
};
enum window_client_sort_type {
WINDOW_CLIENT_BY_NAME,
WINDOW_CLIENT_BY_CREATION_TIME,
WINDOW_CLIENT_BY_ACTIVITY_TIME,
};
static const char *window_client_sort_list[] = {
"name",
"creation time",
"activity time"
};
struct window_client_itemdata {
struct client *c;
};
struct window_client_modedata {
struct mode_tree_data *data;
char *command;
struct window_client_itemdata **item_list;
u_int item_size;
};
static struct window_client_itemdata *
window_client_add_item(struct window_client_modedata *data)
{
struct window_client_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_client_free_item(struct window_client_itemdata *item)
{
server_client_unref(item->c);
free(item);
}
static int
window_client_cmp_name(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
return (strcmp((*a)->c->name, (*b)->c->name));
}
static int
window_client_cmp_creation_time(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, >))
return (-1);
if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, <))
return (1);
return (0);
}
static int
window_client_cmp_activity_time(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, >))
return (-1);
if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, <))
return (1);
return (0);
}
static void
window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag)
{
struct window_client_modedata *data = modedata;
struct window_client_itemdata *item;
u_int i;
struct client *c;
char *tim;
char *text;
for (i = 0; i < data->item_size; i++)
window_client_free_item(data->item_list[i]);
free(data->item_list);
data->item_list = NULL;
data->item_size = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL || (c->flags & (CLIENT_DETACHING)))
continue;
item = window_client_add_item(data);
item->c = c;
c->references++;
}
switch (sort_type) {
case WINDOW_CLIENT_BY_NAME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_name);
break;
case WINDOW_CLIENT_BY_CREATION_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_creation_time);
break;
case WINDOW_CLIENT_BY_ACTIVITY_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_activity_time);
break;
}
for (i = 0; i < data->item_size; i++) {
item = data->item_list[i];
c = item->c;
tim = ctime(&c->activity_time.tv_sec);
*strchr(tim, '\n') = '\0';
xasprintf(&text, "session %s (%s)", c->session->name, tim);
mode_tree_add(data->data, NULL, item, (uint64_t)c, c->name,
text, -1);
free(text);
}
}
static struct screen *
window_client_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
{
struct window_client_itemdata *item = itemdata;
struct client *c = item->c;
struct window_pane *wp;
static struct screen s;
struct screen_write_ctx ctx;
if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING)))
return (NULL);
wp = c->session->curw->window->active;
screen_init(&s, sx, sy, 0);
screen_write_start(&ctx, NULL, &s);
screen_write_clearscreen(&ctx, 8);
screen_write_preview(&ctx, &wp->base, sx, sy - 3);
screen_write_cursormove(&ctx, 0, sy - 2);
screen_write_line(&ctx, sx, 0, 0);
screen_write_cursormove(&ctx, 0, sy - 1);
if (c->old_status != NULL)
screen_write_copy(&ctx, c->old_status, 0, 0, sx, 1, NULL, NULL);
else
screen_write_copy(&ctx, &c->status, 0, 0, sx, 1, NULL, NULL);
screen_write_stop(&ctx);
return (&s);
}
static struct screen *
window_client_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
struct args *args)
{
struct window_client_modedata *data;
struct screen *s;
wp->modedata = data = xcalloc(1, sizeof *data);
if (args == NULL || args->argc == 0)
data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND);
else
data->command = xstrdup(args->argv[0]);
data->data = mode_tree_start(wp, window_client_build,
window_client_draw, data, window_client_sort_list,
nitems(window_client_sort_list), &s);
mode_tree_build(data->data);
mode_tree_draw(data->data);
return (s);
}
static void
window_client_free(struct window_pane *wp)
{
struct window_client_modedata *data = wp->modedata;
u_int i;
if (data == NULL)
return;
mode_tree_free(data->data);
for (i = 0; i < data->item_size; i++)
window_client_free_item(data->item_list[i]);
free(data->item_list);
free(data->command);
free(data);
}
static void
window_client_resize(struct window_pane *wp, u_int sx, u_int sy)
{
struct window_client_modedata *data = wp->modedata;
mode_tree_resize(data->data, sx, sy);
}
static void
window_client_do_detach(void* modedata, void *itemdata, key_code key)
{
struct window_client_modedata *data = modedata;
struct window_client_itemdata *item = itemdata;
if (item == mode_tree_get_current(data->data))
mode_tree_down(data->data, 0);
if (key == 'd' || key == 'D')
server_client_detach(item->c, MSG_DETACH);
else if (key == 'x' || key == 'X')
server_client_detach(item->c, MSG_DETACHKILL);
else if (key == 'z' || key == 'Z')
server_client_suspend(item->c);
}
static void
window_client_key(struct window_pane *wp, struct client *c,
__unused struct session *s, key_code key, struct mouse_event *m)
{
struct window_client_modedata *data = wp->modedata;
struct window_client_itemdata *item;
char *command, *name;
int finished;
/*
* t = toggle tag
* T = tag none
* C-t = tag all
* q = exit
* O = change sort order
*
* d = detach client
* D = detach tagged clients
* x = detach and kill client
* X = detach and kill tagged clients
* z = suspend client
* Z = suspend tagged clients
* Enter = detach client
*/
finished = mode_tree_key(data->data, &key, m);
switch (key) {
case 'd':
case 'x':
case 'z':
item = mode_tree_get_current(data->data);
window_client_do_detach(data, item, key);
mode_tree_build(data->data);
break;
case 'D':
case 'X':
case 'Z':
mode_tree_each_tagged(data->data, window_client_do_detach, key,
0);
mode_tree_build(data->data);
break;
case '\r':
item = mode_tree_get_current(data->data);
command = xstrdup(data->command);
name = xstrdup(item->c->ttyname);
window_pane_reset_mode(wp);
mode_tree_run_command(c, NULL, command, name);
free(name);
free(command);
return;
}
if (finished || server_client_how_many() == 0)
window_pane_reset_mode(wp);
else {
mode_tree_draw(data->data);
wp->flags |= PANE_REDRAW;
}
}

View File

@ -24,7 +24,8 @@
#include "tmux.h"
static struct screen *window_clock_init(struct window_pane *);
static struct screen *window_clock_init(struct window_pane *,
struct cmd_find_state *, struct args *);
static void window_clock_free(struct window_pane *);
static void window_clock_resize(struct window_pane *, u_int, u_int);
static void window_clock_key(struct window_pane *, struct client *,
@ -145,7 +146,8 @@ window_clock_timer_callback(__unused int fd, __unused short events, void *arg)
}
static struct screen *
window_clock_init(struct window_pane *wp)
window_clock_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
__unused struct args *args)
{
struct window_clock_mode_data *data;
struct screen *s;

View File

@ -27,7 +27,8 @@
static const char *window_copy_key_table(struct window_pane *);
static void window_copy_command(struct window_pane *, struct client *,
struct session *, struct args *, struct mouse_event *);
static struct screen *window_copy_init(struct window_pane *);
static struct screen *window_copy_init(struct window_pane *,
struct cmd_find_state *, struct args *);
static void window_copy_free(struct window_pane *);
static int window_copy_pagedown(struct window_pane *, int);
static void window_copy_next_paragraph(struct window_pane *);
@ -187,7 +188,8 @@ struct window_copy_mode_data {
};
static struct screen *
window_copy_init(struct window_pane *wp)
window_copy_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
__unused struct args *args)
{
struct window_copy_mode_data *data;
struct screen *s;

713
window-tree.c Normal file
View File

@ -0,0 +1,713 @@
/* $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 <stdlib.h>
#include <string.h>
#include "tmux.h"
static struct screen *window_tree_init(struct window_pane *,
struct cmd_find_state *, struct args *);
static void window_tree_free(struct window_pane *);
static void window_tree_resize(struct window_pane *, u_int, u_int);
static void window_tree_key(struct window_pane *,
struct client *, struct session *, key_code,
struct mouse_event *);
#define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'"
const struct window_mode window_tree_mode = {
.name = "tree-mode",
.init = window_tree_init,
.free = window_tree_free,
.resize = window_tree_resize,
.key = window_tree_key,
};
enum window_tree_sort_type {
WINDOW_TREE_BY_INDEX,
WINDOW_TREE_BY_NAME,
WINDOW_TREE_BY_TIME,
};
static const char *window_tree_sort_list[] = {
"index",
"name",
"time"
};
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 *command;
struct window_tree_itemdata **item_list;
u_int item_size;
struct client *client;
const char *entered;
char *filter;
struct cmd_find_state fs;
enum window_tree_type type;
};
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 int
window_tree_cmp_session_name(const void *a0, const void *b0)
{
const struct session *const *a = a0;
const struct session *const *b = b0;
return (strcmp((*a)->name, (*b)->name));
}
static int
window_tree_cmp_session_time(const void *a0, const void *b0)
{
const struct session *const *a = a0;
const struct session *const *b = b0;
if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >))
return (-1);
if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <))
return (1);
return (strcmp((*a)->name, (*b)->name));
}
static int
window_tree_cmp_window_name(const void *a0, const void *b0)
{
const struct winlink *const *a = a0;
const struct winlink *const *b = b0;
return (strcmp((*a)->window->name, (*b)->window->name));
}
static int
window_tree_cmp_window_time(const void *a0, const void *b0)
{
const struct winlink *const *a = a0;
const struct winlink *const *b = b0;
if (timercmp(&(*a)->window->activity_time,
&(*b)->window->activity_time, >))
return (-1);
if (timercmp(&(*a)->window->activity_time,
&(*b)->window->activity_time, <))
return (1);
return (strcmp((*a)->window->name, (*b)->window->name));
}
static int
window_tree_cmp_pane_time(const void *a0, const void *b0)
{
const struct window_pane *const *a = a0;
const struct window_pane *const *b = b0;
if ((*a)->active_point < (*b)->active_point)
return (-1);
if ((*a)->active_point > (*b)->active_point)
return (1);
return (0);
}
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;
char *name, *text;
u_int idx;
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;
text = format_single(NULL,
"#{pane_current_command} \"#{pane_title}\"",
NULL, s, wl, wp);
xasprintf(&name, "%u", idx);
mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1);
free(text);
free(name);
}
static int
window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
u_int sort_type, struct mode_tree_item *parent, int no_filter)
{
struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item;
struct mode_tree_item *mti;
char *name, *text, *cp;
struct window_pane *wp, **l;
u_int n, i;
int expanded;
item = window_tree_add_item(data);
item->type = WINDOW_TREE_WINDOW;
item->session = s->id;
item->winlink = wl->idx;
item->pane = -1;
text = format_single(NULL,
"#{window_name}#{window_flags} (#{window_panes} panes)",
NULL, s, wl, NULL);
xasprintf(&name, "%u", wl->idx);
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);
l = NULL;
n = 0;
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
if (!no_filter && data->filter != NULL) {
cp = format_single(NULL, data->filter, NULL, s, wl, wp);
if (!format_true(cp)) {
free(cp);
continue;
}
free(cp);
}
l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = wp;
}
if (n == 0) {
window_tree_free_item(item);
data->item_size--;
mode_tree_remove(data->data, mti);
return (0);
}
switch (sort_type) {
case WINDOW_TREE_BY_INDEX:
break;
case WINDOW_TREE_BY_NAME:
/* Panes don't have names, so leave in number order. */
break;
case WINDOW_TREE_BY_TIME:
qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
break;
}
for (i = 0; i < n; i++)
window_tree_build_pane(s, wl, l[i], modedata, mti);
free(l);
return (1);
}
static void
window_tree_build_session(struct session *s, void* modedata,
u_int sort_type, int no_filter)
{
struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item;
struct mode_tree_item *mti;
char *text;
struct winlink *wl, **l;
u_int n, i, empty;
int expanded;
item = window_tree_add_item(data);
item->type = WINDOW_TREE_SESSION;
item->session = s->id;
item->winlink = -1;
item->pane = -1;
text = format_single(NULL,
"#{session_windows} windows"
"#{?session_grouped, (group ,}"
"#{session_group}#{?session_grouped,),}"
"#{?session_attached, (attached),}",
NULL, s, NULL, NULL);
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 = NULL;
n = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = wl;
}
switch (sort_type) {
case WINDOW_TREE_BY_INDEX:
break;
case WINDOW_TREE_BY_NAME:
qsort(l, n, sizeof *l, window_tree_cmp_window_name);
break;
case WINDOW_TREE_BY_TIME:
qsort(l, n, sizeof *l, window_tree_cmp_window_time);
break;
}
empty = 0;
for (i = 0; i < n; i++) {
if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
no_filter))
empty++;
}
if (empty == n) {
window_tree_free_item(item);
data->item_size--;
mode_tree_remove(data->data, mti);
}
free(l);
}
static void
window_tree_build(void *modedata, u_int sort_type, uint64_t *tag)
{
struct window_tree_modedata *data = modedata;
struct session *s, **l;
u_int n, i;
int no_filter = 0;
restart:
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 = NULL;
n = 0;
RB_FOREACH(s, sessions, &sessions) {
l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = s;
}
switch (sort_type) {
case WINDOW_TREE_BY_INDEX:
break;
case WINDOW_TREE_BY_NAME:
qsort(l, n, sizeof *l, window_tree_cmp_session_name);
break;
case WINDOW_TREE_BY_TIME:
qsort(l, n, sizeof *l, window_tree_cmp_session_time);
break;
}
for (i = 0; i < n; i++)
window_tree_build_session(l[i], modedata, sort_type, no_filter);
free(l);
if (!no_filter && data->item_size == 0) {
no_filter = 1;
goto restart;
}
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:
*tag = (uint64_t)data->fs.wp;
break;
}
}
static struct screen *
window_tree_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
{
struct window_tree_itemdata *item = itemdata;
struct session *sp;
struct winlink *wlp;
struct window_pane *wp;
static struct screen s;
struct screen_write_ctx ctx;
window_tree_pull_item(item, &sp, &wlp, &wp);
if (wp == NULL)
return (NULL);
screen_init(&s, sx, sy, 0);
screen_write_start(&ctx, NULL, &s);
screen_write_preview(&ctx, &wp->base, sx, sy);
screen_write_stop(&ctx);
return (&s);
}
static struct screen *
window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
struct args *args)
{
struct window_tree_modedata *data;
struct screen *s;
wp->modedata = data = xcalloc(1, sizeof *data);
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);
data->wp = wp;
data->references = 1;
if (args_has(args, 'f'))
data->filter = xstrdup(args_get(args, 'f'));
else
data->filter = NULL;
if (args == NULL || args->argc == 0)
data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
else
data->command = xstrdup(args->argv[0]);
data->data = mode_tree_start(wp, window_tree_build,
window_tree_draw, data, window_tree_sort_list,
nitems(window_tree_sort_list), &s);
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;
mode_tree_free(data->data);
for (i = 0; i < data->item_size; i++)
window_tree_free_item(data->item_list[i]);
free(data->item_list);
free(data->filter);
free(data->command);
free(data);
}
static void
window_tree_free(struct window_pane *wp)
{
struct window_tree_modedata *data = wp->modedata;
if (data == NULL)
return;
data->dead = 1;
window_tree_destroy(data);
}
static void
window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
{
struct window_tree_modedata *data = wp->modedata;
mode_tree_resize(data->data, sx, sy);
}
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);
return (target);
}
static void
window_tree_command_each(void* modedata, void* itemdata, __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(data->client, &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 int
window_tree_command_callback(struct client *c, void *modedata, const char *s,
__unused int done)
{
struct window_tree_modedata *data = modedata;
if (data->dead)
return (0);
data->client = c;
data->entered = s;
mode_tree_each_tagged(data->data, window_tree_command_each, KEYC_NONE,
1);
data->client = NULL;
data->entered = NULL;
data->references++;
cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
return (0);
}
static void
window_tree_command_free(void *modedata)
{
struct window_tree_modedata *data = modedata;
window_tree_destroy(data);
}
static int
window_tree_filter_callback(__unused struct client *c, void *modedata,
const char *s, __unused int done)
{
struct window_tree_modedata *data = modedata;
if (data->dead)
return (0);
if (data->filter != NULL)
free(data->filter);
if (s == NULL || *s == '\0')
data->filter = NULL;
else
data->filter = xstrdup(s);
mode_tree_build(data->data);
mode_tree_draw(data->data);
data->wp->flags |= PANE_REDRAW;
return (0);
}
static void
window_tree_filter_free(void *modedata)
{
struct window_tree_modedata *data = modedata;
window_tree_destroy(data);
}
static void
window_tree_key(struct window_pane *wp, struct client *c,
__unused struct session *s, key_code key, struct mouse_event *m)
{
struct window_tree_modedata *data = wp->modedata;
struct window_tree_itemdata *item;
char *command, *name, *prompt;
struct cmd_find_state fs;
int finished;
u_int tagged;
/*
* t = toggle tag
* T = tag none
* C-t = tag all
* q = exit
* O = change sort order
*
* Enter = select item
* : = enter command
* f = enter filter
*/
finished = mode_tree_key(data->data, &key, m);
switch (key) {
case 'f':
data->references++;
status_prompt_set(c, "(filter) ", data->filter,
window_tree_filter_callback, window_tree_filter_free, data,
PROMPT_NOFORMAT);
break;
case ':':
tagged = mode_tree_count_tagged(data->data);
if (tagged != 0)
xasprintf(&prompt, "(%u tagged) ", tagged);
else
xasprintf(&prompt, "(current) ");
data->references++;
status_prompt_set(c, prompt, "", window_tree_command_callback,
window_tree_command_free, data, PROMPT_NOFORMAT);
free(prompt);
break;
case '\r':
item = mode_tree_get_current(data->data);
command = xstrdup(data->command);
name = window_tree_get_target(item, &fs);
window_pane_reset_mode(wp);
if (name != NULL)
mode_tree_run_command(c, &fs, command, name);
free(name);
free(command);
return;
}
if (finished)
window_pane_reset_mode(wp);
else {
mode_tree_draw(data->data);
wp->flags |= PANE_REDRAW;
}
}

View File

@ -1171,7 +1171,8 @@ window_pane_mode_timer(__unused int fd, __unused short events, void *arg)
}
int
window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode,
struct cmd_find_state *fs, struct args *args)
{
struct screen *s;
struct timeval tv = { .tv_sec = 10 };
@ -1184,7 +1185,7 @@ window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
evtimer_set(&wp->modetimer, window_pane_mode_timer, wp);
evtimer_add(&wp->modetimer, &tv);
if ((s = wp->mode->init(wp)) != NULL)
if ((s = wp->mode->init(wp, fs, args)) != NULL)
wp->screen = s;
wp->flags |= (PANE_REDRAW|PANE_CHANGED);
@ -1291,32 +1292,6 @@ window_pane_search(struct window_pane *wp, const char *searchstr)
return (i + 1);
}
char *
window_pane_search_old(struct window_pane *wp, const char *searchstr,
u_int *lineno)
{
struct screen *s = &wp->base;
char *newsearchstr, *line, *msg;
u_int i;
msg = NULL;
xasprintf(&newsearchstr, "*%s*", searchstr);
for (i = 0; i < screen_size_y(s); i++) {
line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
if (fnmatch(newsearchstr, line, 0) == 0) {
msg = line;
if (lineno != NULL)
*lineno = i;
break;
}
free(line);
}
free(newsearchstr);
return (msg);
}
/* Get MRU pane from a list. */
static struct window_pane *
window_pane_choose_best(struct window_pane **list, u_int size)