diff --git a/Makefile b/Makefile index 7f9fdc9d..7eb905ef 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/arguments.c b/arguments.c index 37028648..be918a5a 100644 --- a/arguments.c +++ b/arguments.c @@ -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; diff --git a/cfg.c b/cfg.c index a22b9d68..5f5df24a 100644 --- a/cfg.c +++ b/cfg.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #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]); diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c deleted file mode 100644 index 52ad0ac1..00000000 --- a/cmd-choose-buffer.c +++ /dev/null @@ -1,101 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2010 Nicholas Marriott - * - * 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 - -#include -#include - -#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); -} diff --git a/cmd-choose-client.c b/cmd-choose-client.c deleted file mode 100644 index 88118209..00000000 --- a/cmd-choose-client.c +++ /dev/null @@ -1,135 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2009 Nicholas Marriott - * - * 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 - -#include -#include - -#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); -} diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 28b5013a..f8f24f12 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -18,66 +18,48 @@ #include -#include -#include - -#include - #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); } diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 5599bb96..699a868d 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -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')); } diff --git a/cmd-find-window.c b/cmd-find-window.c index 6338e2e1..92714498 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -18,9 +18,7 @@ #include -#include #include -#include #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(); - } -} diff --git a/cmd-queue.c b/cmd-queue.c index b0885965..8d89e990 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -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); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 2565d6a1..f77fb36a 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -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); diff --git a/cmd-split-window.c b/cmd-split-window.c index 029e6900..4d94cd91 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -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')) { diff --git a/cmd.c b/cmd.c index c61e1e07..ff32d5dd 100644 --- a/cmd.c +++ b/cmd.c @@ -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, diff --git a/format.c b/format.c index 7af7840d..7aad4cb8 100644 --- a/format.c +++ b/format.c @@ -19,11 +19,9 @@ #include #include -#include #include #include #include -#include #include #include #include @@ -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')) diff --git a/key-bindings.c b/key-bindings.c index 11afe0a7..8e184aa0 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -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", diff --git a/mode-tree.c b/mode-tree.c new file mode 100644 index 00000000..f3155fbf --- /dev/null +++ b/mode-tree.c @@ -0,0 +1,705 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2017 Nicholas Marriott + * + * 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 + +#include +#include +#include +#include + +#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(¤t->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); +} diff --git a/options-table.c b/options-table.c index 4d646f73..a3767cc5 100644 --- a/options-table.c +++ b/options-table.c @@ -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 = "," }, diff --git a/screen-write.c b/screen-write.c index 1af623b1..9f684bb3 100644 --- a/screen-write.c +++ b/screen-write.c @@ -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) diff --git a/tmux.1 b/tmux.1 index 8110ae50..a93dbc43 100644 --- a/tmux.1 +++ b/tmux.1 @@ -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 ) diff --git a/tmux.h b/tmux.h index 3f62683f..eb63d11f 100644 --- a/tmux.h +++ b/tmux.h @@ -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 *); diff --git a/window-buffer.c b/window-buffer.c new file mode 100644 index 00000000..f4d4c5f8 --- /dev/null +++ b/window-buffer.c @@ -0,0 +1,342 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2017 Nicholas Marriott + * + * 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 + +#include +#include +#include + +#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; + } +} diff --git a/window-choose.c b/window-choose.c deleted file mode 100644 index 2f3a1631..00000000 --- a/window-choose.c +++ /dev/null @@ -1,1078 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2009 Nicholas Marriott - * - * 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 - -#include -#include -#include - -#include "tmux.h" - -static struct screen *window_choose_init(struct window_pane *); -static void window_choose_free(struct window_pane *); -static void window_choose_resize(struct window_pane *, u_int, u_int); -static void window_choose_key(struct window_pane *, struct client *, - struct session *, key_code, struct mouse_event *); - -static void window_choose_default_callback(struct window_choose_data *); -static struct window_choose_mode_item *window_choose_get_item( - struct window_pane *, key_code, struct mouse_event *); - -static void window_choose_fire_callback(struct window_pane *, - struct window_choose_data *); -static void window_choose_redraw_screen(struct window_pane *); -static void window_choose_write_line(struct window_pane *, - struct screen_write_ctx *, u_int); - -static void window_choose_scroll_up(struct window_pane *); -static void window_choose_scroll_down(struct window_pane *); - -static void window_choose_collapse(struct window_pane *, struct session *, - u_int); -static void window_choose_expand(struct window_pane *, struct session *, - u_int); -static void window_choose_collapse_all(struct window_pane *); - -static void window_choose_data_free(struct window_choose_data *); - -enum window_choose_input_type { - WINDOW_CHOOSE_NORMAL = -1, - WINDOW_CHOOSE_GOTO_ITEM, -}; - -const struct window_mode window_choose_mode = { - .name = "choose-mode", - - .init = window_choose_init, - .free = window_choose_free, - .resize = window_choose_resize, - .key = window_choose_key, -}; - -struct window_choose_mode_item { - struct window_choose_data *wcd; - char *name; - int pos; - int state; -#define TREE_EXPANDED 0x1 -}; - -struct window_choose_mode_data { - struct screen screen; - - struct window_choose_mode_item *list; - u_int list_size; - struct window_choose_mode_item *old_list; - u_int old_list_size; - - int width; - u_int top; - u_int selected; - enum window_choose_input_type input_type; - const char *input_prompt; - char *input_str; - - void (*callbackfn)(struct window_choose_data *); -}; - -static const char window_choose_keys_emacs[] = "0123456789" - "abcdefghijklmnoprstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -static const char window_choose_keys_vi[] = "0123456789" - "abcdefimnoprstuvwxyz" - "ABCDEFIJKMNOPQRSTUVWXYZ"; - -static void window_choose_free1(struct window_choose_mode_data *); -static int window_choose_key_index(struct window_pane *, u_int); -static int window_choose_index_key(struct window_pane *, key_code); -static void window_choose_prompt_input(enum window_choose_input_type, - const char *, struct window_pane *, key_code); -static void window_choose_reset_top(struct window_pane *, u_int); - -void -window_choose_add(struct window_pane *wp, struct window_choose_data *wcd) -{ - struct window_choose_mode_data *data = wp->modedata; - struct window_choose_mode_item *item; - char tmp[11]; - - data->list = xreallocarray(data->list, data->list_size + 1, - sizeof *data->list); - item = &data->list[data->list_size++]; - - item->name = format_expand(wcd->ft, wcd->ft_template); - item->wcd = wcd; - item->pos = data->list_size - 1; - item->state = 0; - - data->width = xsnprintf(tmp, sizeof tmp, "%d", item->pos); -} - -void -window_choose_set_current(struct window_pane *wp, u_int cur) -{ - struct window_choose_mode_data *data = wp->modedata; - struct screen *s = &data->screen; - - data->selected = cur; - window_choose_reset_top(wp, screen_size_y(s)); -} - -static void -window_choose_reset_top(struct window_pane *wp, u_int sy) -{ - struct window_choose_mode_data *data = wp->modedata; - - data->top = 0; - if (data->selected > sy - 1) - data->top = data->selected - (sy - 1); - - window_choose_redraw_screen(wp); -} - -void -window_choose_ready(struct window_pane *wp, u_int cur, - void (*callbackfn)(struct window_choose_data *)) -{ - struct window_choose_mode_data *data = wp->modedata; - u_int size; - - data->callbackfn = callbackfn; - if (data->callbackfn == NULL) - data->callbackfn = window_choose_default_callback; - - size = data->old_list_size; - data->old_list_size += data->list_size; - data->old_list = xreallocarray(data->old_list, data->old_list_size, - sizeof *data->old_list); - memcpy(data->old_list + size, data->list, data->list_size * - sizeof *data->list); - - window_choose_set_current(wp, cur); - window_choose_collapse_all(wp); -} - -static struct screen * -window_choose_init(struct window_pane *wp) -{ - struct window_choose_mode_data *data; - struct screen *s; - - wp->modedata = data = xcalloc(1, sizeof *data); - - data->callbackfn = NULL; - data->input_type = WINDOW_CHOOSE_NORMAL; - data->input_str = xstrdup(""); - data->input_prompt = NULL; - - data->list = NULL; - data->list_size = 0; - - data->old_list = NULL; - data->old_list_size = 0; - - data->top = 0; - - s = &data->screen; - screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); - s->mode &= ~MODE_CURSOR; - - return (s); -} - -struct window_choose_data * -window_choose_data_create(int type, struct client *c, struct session *s) -{ - struct window_choose_data *wcd; - - wcd = xmalloc(sizeof *wcd); - wcd->type = type; - - wcd->ft = format_create(c, NULL, FORMAT_NONE, 0); - wcd->ft_template = NULL; - - wcd->command = NULL; - - wcd->wl = NULL; - wcd->pane_id = -1; - wcd->idx = -1; - - wcd->tree_session = NULL; - - wcd->start_client = c; - wcd->start_client->references++; - wcd->start_session = s; - wcd->start_session->references++; - - return (wcd); -} - -static void -window_choose_data_free(struct window_choose_data *wcd) -{ - server_client_unref(wcd->start_client); - session_remove_ref(wcd->start_session, __func__); - - if (wcd->tree_session != NULL) - session_remove_ref(wcd->tree_session, __func__); - - free(wcd->ft_template); - format_free(wcd->ft); - - free(wcd->command); - free(wcd); -} - -void -window_choose_data_run(struct window_choose_data *cdata) -{ - struct cmd_list *cmdlist; - char *cause; - struct cmdq_item *item; - - /* - * The command template will have already been replaced. But if it's - * NULL, bail here. - */ - if (cdata->command == NULL) - return; - - cmdlist = cmd_string_parse(cdata->command, NULL, 0, &cause); - if (cmdlist == NULL) { - if (cause != NULL) { - *cause = toupper((u_char) *cause); - status_message_set(cdata->start_client, "%s", cause); - free(cause); - } - return; - } - - item = cmdq_get_command(cmdlist, NULL, NULL, 0); - cmdq_append(cdata->start_client, item); - cmd_list_free(cmdlist); -} - -static void -window_choose_default_callback(struct window_choose_data *wcd) -{ - if (wcd == NULL) - return; - if (wcd->start_client->flags & CLIENT_DEAD) - return; - - window_choose_data_run(wcd); -} - -static void -window_choose_free(struct window_pane *wp) -{ - if (wp->modedata != NULL) - window_choose_free1(wp->modedata); -} - -static void -window_choose_free1(struct window_choose_mode_data *data) -{ - struct window_choose_mode_item *item; - u_int i; - - if (data == NULL) - return; - - for (i = 0; i < data->old_list_size; i++) { - item = &data->old_list[i]; - window_choose_data_free(item->wcd); - free(item->name); - } - free(data->list); - free(data->old_list); - - free(data->input_str); - - screen_free(&data->screen); - free(data); -} - -static void -window_choose_resize(struct window_pane *wp, u_int sx, u_int sy) -{ - struct window_choose_mode_data *data = wp->modedata; - struct screen *s = &data->screen; - - window_choose_reset_top(wp, sy); - screen_resize(s, sx, sy, 0); - window_choose_redraw_screen(wp); -} - -static void -window_choose_fire_callback(struct window_pane *wp, - struct window_choose_data *wcd) -{ - struct window_choose_mode_data *data = wp->modedata; - - wp->modedata = NULL; - window_pane_reset_mode(wp); - - data->callbackfn(wcd); - - window_choose_free1(data); -} - -static void -window_choose_prompt_input(enum window_choose_input_type input_type, - const char *prompt, struct window_pane *wp, key_code key) -{ - struct window_choose_mode_data *data = wp->modedata; - size_t input_len; - - data->input_type = input_type; - data->input_prompt = prompt; - input_len = strlen(data->input_str) + 2; - - data->input_str = xrealloc(data->input_str, input_len); - data->input_str[input_len - 2] = key; - data->input_str[input_len - 1] = '\0'; - - window_choose_redraw_screen(wp); -} - -static void -window_choose_collapse(struct window_pane *wp, struct session *s, u_int pos) -{ - struct window_choose_mode_data *data = wp->modedata; - struct window_choose_mode_item *item, *chosen, *copy = NULL; - struct window_choose_data *wcd; - u_int i, copy_size = 0; - - chosen = &data->list[pos]; - chosen->state &= ~TREE_EXPANDED; - - /* - * Trying to mangle the &data->list in-place has lots of problems, so - * assign the actual result we want to render and copy the new one over - * the top of it. - */ - for (i = 0; i < data->list_size; i++) { - item = &data->list[i]; - wcd = item->wcd; - - if (s == wcd->tree_session) { - /* We only show the session when collapsed. */ - if (wcd->type & TREE_SESSION) { - item->state &= ~TREE_EXPANDED; - - copy = xreallocarray(copy, copy_size + 1, - sizeof *copy); - memcpy(©[copy_size], item, sizeof *copy); - copy_size++; - - /* - * Update the selection to this session item so - * we don't end up highlighting a non-existent - * item. - */ - data->selected = i; - } - } else { - copy = xreallocarray(copy, copy_size + 1, sizeof *copy); - memcpy(©[copy_size], item, sizeof *copy); - copy_size++; - } - } - - if (copy_size != 0) { - free(data->list); - data->list = copy; - data->list_size = copy_size; - } -} - -static void -window_choose_collapse_all(struct window_pane *wp) -{ - struct window_choose_mode_data *data = wp->modedata; - struct window_choose_mode_item *item; - struct screen *scr = &data->screen; - struct session *s, *chosen; - u_int i; - - chosen = data->list[data->selected].wcd->start_session; - - RB_FOREACH(s, sessions, &sessions) - window_choose_collapse(wp, s, data->selected); - - /* Reset the selection back to the starting session. */ - for (i = 0; i < data->list_size; i++) { - item = &data->list[i]; - - if (chosen != item->wcd->tree_session) - continue; - - if (item->wcd->type & TREE_SESSION) - data->selected = i; - } - window_choose_reset_top(wp, screen_size_y(scr)); -} - -void -window_choose_expand_all(struct window_pane *wp) -{ - struct window_choose_mode_data *data = wp->modedata; - struct window_choose_mode_item *item; - struct screen *scr = &data->screen; - struct session *s; - u_int i; - - RB_FOREACH(s, sessions, &sessions) { - for (i = 0; i < data->list_size; i++) { - item = &data->list[i]; - - if (s != item->wcd->tree_session) - continue; - - if (item->wcd->type & TREE_SESSION) - window_choose_expand(wp, s, i); - } - } - - window_choose_reset_top(wp, screen_size_y(scr)); -} - -static void -window_choose_expand(struct window_pane *wp, struct session *s, u_int pos) -{ - struct window_choose_mode_data *data = wp->modedata; - struct window_choose_mode_item *item, *chosen; - struct window_choose_data *wcd; - u_int i, items; - - chosen = &data->list[pos]; - items = data->old_list_size - 1; - - /* It's not possible to expand anything other than sessions. */ - if (!(chosen->wcd->type & TREE_SESSION)) - return; - - /* Don't re-expand a session which is already expanded. */ - if (chosen->state & TREE_EXPANDED) - return; - - /* Mark the session entry as expanded. */ - chosen->state |= TREE_EXPANDED; - - /* - * Go back through the original list of all sessions and windows, and - * pull out the windows where the session matches the selection chosen - * to expand. - */ - for (i = items; i > 0; i--) { - item = &data->old_list[i]; - item->state |= TREE_EXPANDED; - wcd = item->wcd; - - if (s == wcd->tree_session) { - /* - * Since the session is already displayed, we only care - * to add back in window for it. - */ - if (wcd->type & TREE_WINDOW) { - /* - * If the insertion point for adding the - * windows to the session falls inside the - * range of the list, then we insert these - * entries in order *AFTER* the selected - * session. - */ - if (pos < i) { - data->list = xreallocarray(data->list, - data->list_size + 1, - sizeof *data->list); - memmove(&data->list[pos + 2], - &data->list[pos + 1], - (data->list_size - (pos + 1)) * - sizeof *data->list); - memcpy(&data->list[pos + 1], - &data->old_list[i], - sizeof *data->list); - data->list_size++; - } else { - /* Ran out of room, add to the end. */ - data->list = xreallocarray(data->list, - data->list_size + 1, - sizeof *data->list); - memcpy(&data->list[data->list_size], - &data->old_list[i], - sizeof *data->list); - data->list_size++; - } - } - } - } -} - -static struct window_choose_mode_item * -window_choose_get_item(struct window_pane *wp, key_code key, - struct mouse_event *m) -{ - struct window_choose_mode_data *data = wp->modedata; - u_int x, y, idx; - - if (!KEYC_IS_MOUSE(key)) - return (&data->list[data->selected]); - - if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) - return (NULL); - - idx = data->top + y; - if (idx >= data->list_size) - return (NULL); - return (&data->list[idx]); -} - -static key_code -window_choose_translate_key(key_code key) -{ - switch (key) { - case '0'|KEYC_ESCAPE: - case '1'|KEYC_ESCAPE: - case '2'|KEYC_ESCAPE: - case '3'|KEYC_ESCAPE: - case '4'|KEYC_ESCAPE: - case '5'|KEYC_ESCAPE: - case '6'|KEYC_ESCAPE: - case '7'|KEYC_ESCAPE: - case '8'|KEYC_ESCAPE: - case '9'|KEYC_ESCAPE: - case '\003': /* C-c */ - case 'q': - case '\n': - case '\r': - case KEYC_BSPACE: - case ' ': - case KEYC_LEFT|KEYC_CTRL: - case KEYC_RIGHT|KEYC_CTRL: - case KEYC_MOUSEDOWN1_PANE: - case KEYC_MOUSEDOWN3_PANE: - case KEYC_WHEELUP_PANE: - case KEYC_WHEELDOWN_PANE: - return (key); - case '\031': /* C-y */ - case KEYC_UP|KEYC_CTRL: - return (KEYC_UP|KEYC_CTRL); - case '\002': /* C-b */ - case KEYC_PPAGE: - return (KEYC_PPAGE); - case '\005': /* C-e */ - case KEYC_DOWN|KEYC_CTRL: - return (KEYC_DOWN|KEYC_CTRL); - case '\006': /* C-f */ - case KEYC_NPAGE: - return (KEYC_NPAGE); - case 'h': - case KEYC_LEFT: - return (KEYC_LEFT); - case 'j': - case KEYC_DOWN: - return (KEYC_DOWN); - case 'k': - case KEYC_UP: - return (KEYC_UP); - case 'l': - case KEYC_RIGHT: - return (KEYC_RIGHT); - case 'g': - case KEYC_HOME: - return (KEYC_HOME); - case 'G': - case KEYC_END: - return (KEYC_END); - case 'H': - return ('R'|KEYC_ESCAPE); - case 'L': - return ('r'|KEYC_ESCAPE); - } - if ((key >= '0' && key <= '9') || - (key >= 'a' && key <= 'z') || - (key >= 'A' && key <= 'Z')) - return (key); - return (KEYC_NONE); -} - -static void -window_choose_key(struct window_pane *wp, __unused struct client *c, - __unused struct session *sp, key_code key, struct mouse_event *m) -{ - struct window_choose_mode_data *data = wp->modedata; - struct screen *s = &data->screen; - struct screen_write_ctx ctx; - struct window_choose_mode_item *item; - size_t input_len; - u_int items, n; - int idx, keys; - - keys = options_get_number(wp->window->options, "mode-keys"); - if (keys == MODEKEY_VI) { - key = window_choose_translate_key(key); - if (key == KEYC_NONE) - return; - } - items = data->list_size; - - if (data->input_type == WINDOW_CHOOSE_GOTO_ITEM) { - switch (key) { - case '\003': /* C-c */ - case '\033': /* Escape */ - case 'q': - data->input_type = WINDOW_CHOOSE_NORMAL; - window_choose_redraw_screen(wp); - break; - case '\n': - case '\r': - n = strtonum(data->input_str, 0, INT_MAX, NULL); - if (n > items - 1) { - data->input_type = WINDOW_CHOOSE_NORMAL; - window_choose_redraw_screen(wp); - break; - } - window_choose_fire_callback(wp, data->list[n].wcd); - break; - case KEYC_BSPACE: - input_len = strlen(data->input_str); - if (input_len > 0) - data->input_str[input_len - 1] = '\0'; - window_choose_redraw_screen(wp); - break; - default: - if (key < '0' || key > '9') - break; - window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM, - "Goto Item", wp, key); - break; - } - return; - } - - switch (key) { - case '\003': /* C-c */ - case '\033': /* Escape */ - case 'q': - window_choose_fire_callback(wp, NULL); - break; - case '\n': - case '\r': - case KEYC_MOUSEDOWN1_PANE: - if ((item = window_choose_get_item(wp, key, m)) == NULL) - break; - window_choose_fire_callback(wp, item->wcd); - break; - case ' ': - case KEYC_MOUSEDOWN3_PANE: - if ((item = window_choose_get_item(wp, key, m)) == NULL) - break; - if (item->state & TREE_EXPANDED) { - window_choose_collapse(wp, item->wcd->tree_session, - data->selected); - } else { - window_choose_expand(wp, item->wcd->tree_session, - data->selected); - } - window_choose_redraw_screen(wp); - break; - case KEYC_LEFT: - if ((item = window_choose_get_item(wp, key, m)) == NULL) - break; - if (item->state & TREE_EXPANDED) { - window_choose_collapse(wp, item->wcd->tree_session, - data->selected); - window_choose_redraw_screen(wp); - } - break; - case KEYC_LEFT|KEYC_CTRL: - window_choose_collapse_all(wp); - break; - case KEYC_RIGHT: - if ((item = window_choose_get_item(wp, key, m)) == NULL) - break; - if (!(item->state & TREE_EXPANDED)) { - window_choose_expand(wp, item->wcd->tree_session, - data->selected); - window_choose_redraw_screen(wp); - } - break; - case KEYC_RIGHT|KEYC_CTRL: - window_choose_expand_all(wp); - break; - case '\020': /* C-p */ - case KEYC_UP: - case KEYC_WHEELUP_PANE: - if (items == 0) - break; - if (data->selected == 0) { - data->selected = items - 1; - if (data->selected > screen_size_y(s) - 1) - data->top = items - screen_size_y(s); - window_choose_redraw_screen(wp); - break; - } - data->selected--; - if (data->selected < data->top) - window_choose_scroll_up(wp); - else { - screen_write_start(&ctx, wp, NULL); - window_choose_write_line(wp, &ctx, - data->selected - data->top); - window_choose_write_line(wp, &ctx, - data->selected + 1 - data->top); - screen_write_stop(&ctx); - } - break; - case '\016': /* C-n */ - case KEYC_DOWN: - case KEYC_WHEELDOWN_PANE: - if (items == 0) - break; - if (data->selected == items - 1) { - data->selected = 0; - data->top = 0; - window_choose_redraw_screen(wp); - break; - } - data->selected++; - - if (data->selected < data->top + screen_size_y(s)) { - screen_write_start(&ctx, wp, NULL); - window_choose_write_line(wp, &ctx, - data->selected - data->top); - window_choose_write_line(wp, &ctx, - data->selected - 1 - data->top); - screen_write_stop(&ctx); - } else - window_choose_scroll_down(wp); - break; - case KEYC_UP|KEYC_CTRL: - if (items == 0 || data->top == 0) - break; - if (data->selected == data->top + screen_size_y(s) - 1) { - data->selected--; - window_choose_scroll_up(wp); - screen_write_start(&ctx, wp, NULL); - window_choose_write_line(wp, &ctx, - screen_size_y(s) - 1); - screen_write_stop(&ctx); - } else - window_choose_scroll_up(wp); - break; - case KEYC_DOWN|KEYC_CTRL: - if (items == 0 || - data->top + screen_size_y(&data->screen) >= items) - break; - if (data->selected == data->top) { - data->selected++; - window_choose_scroll_down(wp); - screen_write_start(&ctx, wp, NULL); - window_choose_write_line(wp, &ctx, 0); - screen_write_stop(&ctx); - } else - window_choose_scroll_down(wp); - break; - case KEYC_PPAGE: - if (data->selected < screen_size_y(s)) { - data->selected = 0; - data->top = 0; - } else { - data->selected -= screen_size_y(s); - if (data->top < screen_size_y(s)) - data->top = 0; - else - data->top -= screen_size_y(s); - } - window_choose_redraw_screen(wp); - break; - case KEYC_NPAGE: - data->selected += screen_size_y(s); - if (data->selected > items - 1) - data->selected = items - 1; - data->top += screen_size_y(s); - if (screen_size_y(s) < items) { - if (data->top + screen_size_y(s) > items) - data->top = items - screen_size_y(s); - } else - data->top = 0; - if (data->selected < data->top) - data->top = data->selected; - window_choose_redraw_screen(wp); - break; - case KEYC_BSPACE: - input_len = strlen(data->input_str); - if (input_len > 0) - data->input_str[input_len - 1] = '\0'; - window_choose_redraw_screen(wp); - break; - case '0'|KEYC_ESCAPE: - case '1'|KEYC_ESCAPE: - case '2'|KEYC_ESCAPE: - case '3'|KEYC_ESCAPE: - case '4'|KEYC_ESCAPE: - case '5'|KEYC_ESCAPE: - case '6'|KEYC_ESCAPE: - case '7'|KEYC_ESCAPE: - case '8'|KEYC_ESCAPE: - case '9'|KEYC_ESCAPE: - key &= KEYC_MASK_KEY; - if (key < '0' || key > '9') - break; - window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM, - "Goto Item", wp, key); - break; - case KEYC_HOME: - case '<'|KEYC_ESCAPE: - data->selected = 0; - data->top = 0; - window_choose_redraw_screen(wp); - break; - case 'R'|KEYC_ESCAPE: - data->selected = data->top; - window_choose_redraw_screen(wp); - break; - case 'r'|KEYC_ESCAPE: - data->selected = data->top + screen_size_y(s) - 1; - if (data->selected > items - 1) - data->selected = items - 1; - window_choose_redraw_screen(wp); - break; - case KEYC_END: - case '>'|KEYC_ESCAPE: - data->selected = items - 1; - if (screen_size_y(s) < items) - data->top = items - screen_size_y(s); - else - data->top = 0; - window_choose_redraw_screen(wp); - break; - default: - idx = window_choose_index_key(wp, key); - if (idx < 0 || (u_int) idx >= data->list_size) - break; - data->selected = idx; - window_choose_fire_callback(wp, data->list[idx].wcd); - break; - } -} - -static void -window_choose_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, - u_int py) -{ - struct window_choose_mode_data *data = wp->modedata; - struct window_choose_mode_item *item; - struct options *oo = wp->window->options; - struct screen *s = &data->screen; - struct grid_cell gc; - size_t last, xoff = 0; - char hdr[32], label[32]; - int key; - - if (data->callbackfn == NULL) - fatalx("called before callback assigned"); - - last = screen_size_y(s) - 1; - memcpy(&gc, &grid_default_cell, sizeof gc); - gc.flags |= GRID_FLAG_NOPALETTE; - if (data->selected == data->top + py) - style_apply(&gc, oo, "mode-style"); - - screen_write_cursormove(ctx, 0, py); - if (data->top + py < data->list_size) { - item = &data->list[data->top + py]; - if (item->wcd->wl != NULL && - item->wcd->wl->flags & WINLINK_ALERTFLAGS) - gc.attr |= GRID_ATTR_BRIGHT; - - key = window_choose_key_index(wp, data->top + py); - if (key != -1) - xsnprintf(label, sizeof label, "(%c)", key); - else - xsnprintf(label, sizeof label, "(%d)", item->pos); - screen_write_nputs(ctx, screen_size_x(s) - 1, &gc, - "%*s %s %s", data->width + 2, label, - /* - * Add indication to tree if necessary about whether it's - * expanded or not. - */ - (item->wcd->type & TREE_SESSION) ? - ((item->state & TREE_EXPANDED) ? "-" : "+") : "", item->name); - } - while (s->cx < screen_size_x(s) - 1) - screen_write_putc(ctx, &gc, ' '); - - if (data->input_type != WINDOW_CHOOSE_NORMAL) { - style_apply(&gc, oo, "mode-style"); - - xoff = xsnprintf(hdr, sizeof hdr, - "%s: %s", data->input_prompt, data->input_str); - screen_write_cursormove(ctx, 0, last); - screen_write_puts(ctx, &gc, "%s", hdr); - screen_write_cursormove(ctx, xoff, py); - memcpy(&gc, &grid_default_cell, sizeof gc); - } - -} - -static int -window_choose_key_index(struct window_pane *wp, u_int idx) -{ - const char *ptr; - int keys; - - keys = options_get_number(wp->window->options, "mode-keys"); - if (keys == MODEKEY_VI) - ptr = window_choose_keys_vi; - else - ptr = window_choose_keys_emacs; - for (; *ptr != '\0'; ptr++) { - if (idx-- == 0) - return (*ptr); - } - return (-1); -} - -static int -window_choose_index_key(struct window_pane *wp, key_code key) -{ - const char *ptr; - int keys; - u_int idx = 0; - - keys = options_get_number(wp->window->options, "mode-keys"); - if (keys == MODEKEY_VI) - ptr = window_choose_keys_vi; - else - ptr = window_choose_keys_emacs; - for (; *ptr != '\0'; ptr++) { - if (key == (key_code)*ptr) - return (idx); - idx++; - } - return (-1); -} - -static void -window_choose_redraw_screen(struct window_pane *wp) -{ - struct window_choose_mode_data *data = wp->modedata; - struct screen *s = &data->screen; - struct screen_write_ctx ctx; - u_int i; - - screen_write_start(&ctx, wp, NULL); - for (i = 0; i < screen_size_y(s); i++) - window_choose_write_line(wp, &ctx, i); - screen_write_stop(&ctx); -} - -static void -window_choose_scroll_up(struct window_pane *wp) -{ - struct window_choose_mode_data *data = wp->modedata; - struct screen_write_ctx ctx; - - if (data->top == 0) - return; - data->top--; - - screen_write_start(&ctx, wp, NULL); - screen_write_cursormove(&ctx, 0, 0); - screen_write_insertline(&ctx, 1, 8); - window_choose_write_line(wp, &ctx, 0); - if (screen_size_y(&data->screen) > 1) - window_choose_write_line(wp, &ctx, 1); - screen_write_stop(&ctx); -} - -static void -window_choose_scroll_down(struct window_pane *wp) -{ - struct window_choose_mode_data *data = wp->modedata; - struct screen *s = &data->screen; - struct screen_write_ctx ctx; - - if (data->top >= data->list_size) - return; - data->top++; - - screen_write_start(&ctx, wp, NULL); - screen_write_cursormove(&ctx, 0, 0); - screen_write_deleteline(&ctx, 1, 8); - window_choose_write_line(wp, &ctx, screen_size_y(s) - 1); - if (screen_size_y(&data->screen) > 1) - window_choose_write_line(wp, &ctx, screen_size_y(s) - 2); - screen_write_stop(&ctx); -} - -struct window_choose_data * -window_choose_add_session(struct window_pane *wp, struct client *c, - struct session *s, const char *template, const char *action, u_int idx) -{ - struct window_choose_data *wcd; - - wcd = window_choose_data_create(TREE_SESSION, c, c->session); - wcd->idx = s->id; - - wcd->tree_session = s; - wcd->tree_session->references++; - - wcd->ft_template = xstrdup(template); - format_add(wcd->ft, "line", "%u", idx); - format_defaults(wcd->ft, NULL, s, NULL, NULL); - - wcd->command = cmd_template_replace(action, s->name, 1); - - window_choose_add(wp, wcd); - - return (wcd); -} - -struct window_choose_data * -window_choose_add_window(struct window_pane *wp, struct client *c, - struct session *s, struct winlink *wl, const char *template, - const char *action, u_int idx) -{ - struct window_choose_data *wcd; - char *expanded; - - wcd = window_choose_data_create(TREE_WINDOW, c, c->session); - wcd->idx = wl->idx; - - wcd->wl = wl; - - wcd->tree_session = s; - wcd->tree_session->references++; - - wcd->ft_template = xstrdup(template); - format_add(wcd->ft, "line", "%u", idx); - format_defaults(wcd->ft, NULL, s, wl, NULL); - - xasprintf(&expanded, "%s:%d", s->name, wl->idx); - wcd->command = cmd_template_replace(action, expanded, 1); - free(expanded); - - window_choose_add(wp, wcd); - - return (wcd); -} diff --git a/window-client.c b/window-client.c new file mode 100644 index 00000000..4d720960 --- /dev/null +++ b/window-client.c @@ -0,0 +1,335 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2017 Nicholas Marriott + * + * 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 +#include + +#include +#include + +#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; + } +} diff --git a/window-clock.c b/window-clock.c index d23ac136..9ecc68a1 100644 --- a/window-clock.c +++ b/window-clock.c @@ -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; diff --git a/window-copy.c b/window-copy.c index c8807c99..def4b923 100644 --- a/window-copy.c +++ b/window-copy.c @@ -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; diff --git a/window-tree.c b/window-tree.c new file mode 100644 index 00000000..36a19e48 --- /dev/null +++ b/window-tree.c @@ -0,0 +1,713 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2017 Nicholas Marriott + * + * 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 + +#include +#include + +#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; + } +} diff --git a/window.c b/window.c index 5e49adc4..0ca0c72c 100644 --- a/window.c +++ b/window.c @@ -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)