Rewrite of choose mode, both to simplify and tidy the code and to add
authornicm <nicm@openbsd.org>
Tue, 30 May 2017 21:44:59 +0000 (21:44 +0000)
committernicm <nicm@openbsd.org>
Tue, 30 May 2017 21:44:59 +0000 (21:44 +0000)
some modern features.

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

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

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

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

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

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

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

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

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

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

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

Parts written by Thomas Adam.

26 files changed:
usr.bin/tmux/Makefile
usr.bin/tmux/arguments.c
usr.bin/tmux/cfg.c
usr.bin/tmux/cmd-choose-buffer.c [deleted file]
usr.bin/tmux/cmd-choose-client.c [deleted file]
usr.bin/tmux/cmd-choose-tree.c
usr.bin/tmux/cmd-copy-mode.c
usr.bin/tmux/cmd-find-window.c
usr.bin/tmux/cmd-queue.c
usr.bin/tmux/cmd-run-shell.c
usr.bin/tmux/cmd-split-window.c
usr.bin/tmux/cmd.c
usr.bin/tmux/format.c
usr.bin/tmux/key-bindings.c
usr.bin/tmux/mode-tree.c [new file with mode: 0644]
usr.bin/tmux/options-table.c
usr.bin/tmux/screen-write.c
usr.bin/tmux/tmux.1
usr.bin/tmux/tmux.h
usr.bin/tmux/window-buffer.c [new file with mode: 0644]
usr.bin/tmux/window-choose.c [deleted file]
usr.bin/tmux/window-client.c [new file with mode: 0644]
usr.bin/tmux/window-clock.c
usr.bin/tmux/window-copy.c
usr.bin/tmux/window-tree.c [new file with mode: 0644]
usr.bin/tmux/window.c

index 81d5fbc..1d3fca6 100644 (file)
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.87 2017/04/20 17:49:26 nicm Exp $
+# $OpenBSD: Makefile,v 1.88 2017/05/30 21:44:59 nicm Exp $
 
 PROG=  tmux
 SRCS=  alerts.c \
@@ -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
index f17f15a..ac15296 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: arguments.c,v 1.18 2017/04/22 12:08:41 nicm Exp $ */
+/* $OpenBSD: arguments.c,v 1.19 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -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;
index 6257930..c3f59b2 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cfg.c,v 1.59 2017/05/01 12:20:55 nicm Exp $ */
+/* $OpenBSD: cfg.c,v 1.60 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -23,7 +23,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 #include <util.h>
 
 #include "tmux.h"
@@ -234,7 +233,7 @@ cfg_show_causes(struct session *s)
                return;
        wp = s->curw->window->active;
 
-       window_pane_set_mode(wp, &window_copy_mode);
+       window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
        window_copy_init_for_output(wp);
        for (i = 0; i < cfg_ncauses; i++) {
                window_copy_add(wp, "%s", cfg_causes[i]);
diff --git a/usr.bin/tmux/cmd-choose-buffer.c b/usr.bin/tmux/cmd-choose-buffer.c
deleted file mode 100644 (file)
index 9f1e943..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/* $OpenBSD: cmd-choose-buffer.c,v 1.32 2017/04/22 10:22:39 nicm Exp $ */
-
-/*
- * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-
-#include <ctype.h>
-#include <stdlib.h>
-
-#include "tmux.h"
-
-/*
- * Enter choice mode to choose a buffer.
- */
-
-#define CHOOSE_BUFFER_TEMPLATE                                         \
-       "#{buffer_name}: #{buffer_size} bytes: #{buffer_sample}"
-
-static enum cmd_retval cmd_choose_buffer_exec(struct cmd *,
-                           struct cmdq_item *);
-
-const struct cmd_entry cmd_choose_buffer_entry = {
-       .name = "choose-buffer",
-       .alias = NULL,
-
-       .args = { "F:t:", 0, 1 },
-       .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
-
-       .target = { 't', CMD_FIND_WINDOW, 0 },
-
-       .flags = 0,
-       .exec = cmd_choose_buffer_exec
-};
-
-static enum cmd_retval
-cmd_choose_buffer_exec(struct cmd *self, struct cmdq_item *item)
-{
-       struct args                     *args = self->args;
-       struct client                   *c = cmd_find_client(item, NULL, 1);
-       struct winlink                  *wl = item->target.wl;
-       struct window_choose_data       *cdata;
-       struct paste_buffer             *pb;
-       char                            *action, *action_data;
-       const char                      *template;
-       u_int                            idx;
-
-       if (c == NULL) {
-               cmdq_error(item, "no client available");
-               return (CMD_RETURN_ERROR);
-       }
-
-       if ((template = args_get(args, 'F')) == NULL)
-               template = CHOOSE_BUFFER_TEMPLATE;
-
-       if (paste_get_top(NULL) == NULL)
-               return (CMD_RETURN_NORMAL);
-
-       if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
-               return (CMD_RETURN_NORMAL);
-
-       if (args->argc != 0)
-               action = xstrdup(args->argv[0]);
-       else
-               action = xstrdup("paste-buffer -b '%%'");
-
-       idx = 0;
-       pb = NULL;
-       while ((pb = paste_walk(pb)) != NULL) {
-               cdata = window_choose_data_create(TREE_OTHER, c, c->session);
-               cdata->idx = idx;
-
-               cdata->ft_template = xstrdup(template);
-               format_defaults_paste_buffer(cdata->ft, pb);
-
-               xasprintf(&action_data, "%s", paste_buffer_name(pb));
-               cdata->command = cmd_template_replace(action, action_data, 1);
-               free(action_data);
-
-               window_choose_add(wl->window->active, cdata);
-               idx++;
-       }
-       free(action);
-
-       window_choose_ready(wl->window->active, 0, NULL);
-
-       return (CMD_RETURN_NORMAL);
-}
diff --git a/usr.bin/tmux/cmd-choose-client.c b/usr.bin/tmux/cmd-choose-client.c
deleted file mode 100644 (file)
index 6cb91c0..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/* $OpenBSD: cmd-choose-client.c,v 1.33 2017/04/22 10:22:39 nicm Exp $ */
-
-/*
- * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-
-#include <ctype.h>
-#include <stdlib.h>
-
-#include "tmux.h"
-
-/*
- * Enter choice mode to choose a client.
- */
-
-#define CHOOSE_CLIENT_TEMPLATE                                 \
-       "#{client_name}: #{session_name} "                      \
-       "[#{client_width}x#{client_height} #{client_termname}]" \
-       "#{?client_utf8, (utf8),}#{?client_readonly, (ro),} "   \
-       "(last used #{t:client_activity})"
-
-static enum cmd_retval cmd_choose_client_exec(struct cmd *,
-                           struct cmdq_item *);
-
-static void    cmd_choose_client_callback(struct window_choose_data *);
-
-const struct cmd_entry cmd_choose_client_entry = {
-       .name = "choose-client",
-       .alias = NULL,
-
-       .args = { "F:t:", 0, 1 },
-       .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
-
-       .target = { 't', CMD_FIND_WINDOW, 0 },
-
-       .flags = 0,
-       .exec = cmd_choose_client_exec
-};
-
-struct cmd_choose_client_data {
-       struct client   *client;
-};
-
-static enum cmd_retval
-cmd_choose_client_exec(struct cmd *self, struct cmdq_item *item)
-{
-       struct args                     *args = self->args;
-       struct client                   *c = cmd_find_client(item, NULL, 1);
-       struct client                   *c1;
-       struct window_choose_data       *cdata;
-       struct winlink                  *wl = item->target.wl;
-       const char                      *template;
-       char                            *action;
-       u_int                            idx, cur;
-
-       if (c == NULL) {
-               cmdq_error(item, "no client available");
-               return (CMD_RETURN_ERROR);
-       }
-
-       if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
-               return (CMD_RETURN_NORMAL);
-
-       if ((template = args_get(args, 'F')) == NULL)
-               template = CHOOSE_CLIENT_TEMPLATE;
-
-       if (args->argc != 0)
-               action = xstrdup(args->argv[0]);
-       else
-               action = xstrdup("detach-client -t '%%'");
-
-       cur = idx = 0;
-       TAILQ_FOREACH(c1, &clients, entry) {
-               if (c1->session == NULL)
-                       continue;
-               if (c1 == item->client)
-                       cur = idx;
-
-               cdata = window_choose_data_create(TREE_OTHER, c, c->session);
-               cdata->idx = idx;
-
-               cdata->ft_template = xstrdup(template);
-               format_add(cdata->ft, "line", "%u", idx);
-               format_defaults(cdata->ft, c1, NULL, NULL, NULL);
-
-               cdata->command = cmd_template_replace(action, c1->name, 1);
-
-               window_choose_add(wl->window->active, cdata);
-
-               idx++;
-       }
-       free(action);
-
-       window_choose_ready(wl->window->active, cur,
-           cmd_choose_client_callback);
-
-       return (CMD_RETURN_NORMAL);
-}
-
-static void
-cmd_choose_client_callback(struct window_choose_data *cdata)
-{
-       struct client   *c;
-       u_int            idx;
-
-       if (cdata == NULL)
-               return;
-       if (cdata->start_client->flags & CLIENT_DEAD)
-               return;
-
-       idx = 0;
-       TAILQ_FOREACH(c, &clients, entry) {
-               if (idx == cdata->idx)
-                       break;
-               idx++;
-       }
-       if (c == NULL || c->session == NULL)
-               return;
-
-       window_choose_data_run(cdata);
-}
index 50f882b..354fa91 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-choose-tree.c,v 1.34 2017/04/22 10:22:39 nicm Exp $ */
+/* $OpenBSD: cmd-choose-tree.c,v 1.35 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
 
 #include <sys/types.h>
 
-#include <ctype.h>
-#include <stdlib.h>
-
-#include <string.h>
-
 #include "tmux.h"
 
-#define CMD_CHOOSE_TREE_WINDOW_ACTION "select-window -t '%%'"
-#define CMD_CHOOSE_TREE_SESSION_ACTION "switch-client -t '%%'"
-
 /*
- * Enter choice mode to choose a session and/or window.
+ * Enter a mode.
  */
 
-#define CHOOSE_TREE_SESSION_TEMPLATE                           \
-       "#{session_name}: #{session_windows} windows"           \
-       "#{?session_grouped, (group ,}"                         \
-       "#{session_group}#{?session_grouped,),}"                \
-       "#{?session_attached, (attached),}"
-#define CHOOSE_TREE_WINDOW_TEMPLATE                            \
-       "#{window_index}: #{window_name}#{window_flags} "       \
-       "\"#{pane_title}\""
-
 static enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmdq_item *);
 
 const struct cmd_entry cmd_choose_tree_entry = {
        .name = "choose-tree",
        .alias = NULL,
 
-       .args = { "S:W:swub:c:t:", 0, 1 },
-       .usage = "[-suw] [-b session-template] [-c window template] "
-                "[-S format] [-W format] " CMD_TARGET_WINDOW_USAGE,
+       .args = { "st:w", 0, 1 },
+       .usage = "[-sw] " CMD_TARGET_PANE_USAGE,
 
-       .target = { 't', CMD_FIND_WINDOW, 0 },
+       .target = { 't', CMD_FIND_PANE, 0 },
 
        .flags = 0,
        .exec = cmd_choose_tree_exec
 };
 
-const struct cmd_entry cmd_choose_session_entry = {
-       .name = "choose-session",
+const struct cmd_entry cmd_choose_client_entry = {
+       .name = "choose-client",
        .alias = NULL,
 
-       .args = { "F:t:", 0, 1 },
-       .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
+       .args = { "t:", 0, 1 },
+       .usage = CMD_TARGET_PANE_USAGE,
 
-       .target = { 't', CMD_FIND_WINDOW, 0 },
+       .target = { 't', CMD_FIND_PANE, 0 },
 
        .flags = 0,
        .exec = cmd_choose_tree_exec
 };
 
-const struct cmd_entry cmd_choose_window_entry = {
-       .name = "choose-window",
+const struct cmd_entry cmd_choose_buffer_entry = {
+       .name = "choose-buffer",
        .alias = NULL,
 
-       .args = { "F:t:", 0, 1 },
-       .usage = CMD_TARGET_WINDOW_USAGE "[-F format] [template]",
+       .args = { "t:", 0, 1 },
+       .usage = CMD_TARGET_PANE_USAGE,
 
-       .target = { 't', CMD_FIND_WINDOW, 0 },
+       .target = { 't', CMD_FIND_PANE, 0 },
 
        .flags = 0,
        .exec = cmd_choose_tree_exec
@@ -87,167 +69,20 @@ static enum cmd_retval
 cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
 {
        struct args                     *args = self->args;
-       struct client                   *c = cmd_find_client(item, NULL, 1);
-       struct winlink                  *wl = item->target.wl, *wm;
-       struct session                  *s = item->target.s, *s2;
-       struct window_choose_data       *wcd = NULL;
-       const char                      *ses_template, *win_template;
-       char                            *final_win_action, *cur_win_template;
-       char                            *final_win_template_middle;
-       char                            *final_win_template_last;
-       const char                      *ses_action, *win_action;
-       u_int                            cur_win, idx_ses, win_ses, win_max;
-       u_int                            wflag, sflag;
-
-       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);
+       struct window_pane              *wp = item->target.wp;
+       const struct window_mode        *mode;
+
+       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);
 }
index ac1a8e7..6a957da 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-copy-mode.c,v 1.33 2017/04/22 10:22:39 nicm Exp $ */
+/* $OpenBSD: cmd-copy-mode.c,v 1.34 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -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'));
        }
index 68a52f3..8809b55 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-find-window.c,v 1.42 2017/05/29 18:06:34 nicm Exp $ */
+/* $OpenBSD: cmd-find-window.c,v 1.43 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -18,9 +18,7 @@
 
 #include <sys/types.h>
 
-#include <fnmatch.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include "tmux.h"
 
  * 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;
-
-       if (c == NULL) {
-               cmdq_error(item, "no client available");
-               return (CMD_RETURN_ERROR);
-       }
-
-       if ((template = args_get(args, 'F')) == NULL)
-               template = FIND_WINDOW_TEMPLATE;
-
-       match_flags = cmd_find_window_match_flags(args);
-       str = args->argv[0];
-
-       TAILQ_INIT(&find_list);
-
-       xasprintf(&searchstr, "*%s*", str);
-       RB_FOREACH(wm, winlinks, &s->windows)
-           cmd_find_window_match(&find_list, match_flags, wm, str, searchstr);
-       free(searchstr);
-
-       if (TAILQ_EMPTY(&find_list)) {
-               cmdq_error(item, "no windows matching: %s", str);
-               return (CMD_RETURN_ERROR);
-       }
-
-       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;
+       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;
+
+       C = args_has(args, 'C');
+       N = args_has(args, 'N');
+       T = args_has(args, 'T');
+
+       if (!C && !N && !T)
+               C = N = T = 1;
+
+       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);
+
+       new_args = args_parse("", 1, &argv);
+       args_set(new_args, 'f', filter);
+
+       window_pane_set_mode(wp, &window_tree_mode, &item->target, new_args);
+
+       args_free(new_args);
+       free(filter);
 
-       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();
-       }
+       return (CMD_RETURN_NORMAL);
 }
index 68109e5..9b9e52c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-queue.c,v 1.55 2017/05/01 12:20:55 nicm Exp $ */
+/* $OpenBSD: cmd-queue.c,v 1.56 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -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);
index c85220c..18cd0db 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-run-shell.c,v 1.49 2017/04/22 10:22:39 nicm Exp $ */
+/* $OpenBSD: cmd-run-shell.c,v 1.50 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -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);
index fa42ba4..83490ab 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-split-window.c,v 1.84 2017/04/25 15:35:10 nicm Exp $ */
+/* $OpenBSD: cmd-split-window.c,v 1.85 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -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')) {
index 61e5569..ac69f21 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd.c,v 1.138 2017/04/22 10:22:39 nicm Exp $ */
+/* $OpenBSD: cmd.c,v 1.139 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -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,
index 7ce43b6..0853b9f 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: format.c,v 1.140 2017/05/29 18:06:34 nicm Exp $ */
+/* $OpenBSD: format.c,v 1.141 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
 #include <sys/types.h>
 #include <sys/wait.h>
 
-#include <ctype.h>
 #include <errno.h>
 #include <fnmatch.h>
 #include <libgen.h>
-#include <netdb.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
@@ -840,7 +838,7 @@ format_choose(char *s, char **left, char **right)
 }
 
 /* Is this true? */
-static int
+int
 format_true(const char *s)
 {
        if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))
index ff5530f..dd88266 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: key-bindings.c,v 1.79 2017/05/30 08:13:48 nicm Exp $ */
+/* $OpenBSD: key-bindings.c,v 1.80 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -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/usr.bin/tmux/mode-tree.c b/usr.bin/tmux/mode-tree.c
new file mode 100644 (file)
index 0000000..b6e3b6b
--- /dev/null
@@ -0,0 +1,705 @@
+/* $OpenBSD: mode-tree.c,v 1.1 2017/05/30 21:44:59 nicm Exp $ */
+
+/*
+ * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+struct mode_tree_item;
+TAILQ_HEAD(mode_tree_list, mode_tree_item);
+
+struct mode_tree_data {
+       struct window_pane       *wp;
+       void                     *modedata;
+
+       const char              **sort_list;
+       u_int                     sort_size;
+       u_int                     sort_type;
+
+       void                     (*buildcb)(void *, u_int, uint64_t *);
+       struct screen           *(*drawcb)(void *, void *, u_int, u_int);
+
+       struct mode_tree_list     children;
+       struct mode_tree_list     saved;
+
+       struct mode_tree_line    *line_list;
+       u_int                     line_size;
+
+       u_int                     depth;
+
+       u_int                     width;
+       u_int                     height;
+
+       u_int                     offset;
+       u_int                     current;
+
+       struct screen             screen;
+};
+
+struct mode_tree_item {
+       struct mode_tree_item           *parent;
+       void                            *itemdata;
+       u_int                            line;
+
+       uint64_t                         tag;
+       const char                      *name;
+       const char                      *text;
+
+       int                              expanded;
+       int                              tagged;
+
+       struct mode_tree_list            children;
+       TAILQ_ENTRY(mode_tree_item)      entry;
+};
+
+struct mode_tree_line {
+       struct mode_tree_item           *item;
+       u_int                            depth;
+       int                              last;
+       int                              flat;
+};
+
+static void mode_tree_free_items(struct mode_tree_list *);
+
+static struct mode_tree_item *
+mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
+{
+       struct mode_tree_item   *mti, *child;
+
+       TAILQ_FOREACH(mti, mtl, entry) {
+               if (mti->tag == tag)
+                       return (mti);
+               child = mode_tree_find_item(&mti->children, tag);
+               if (child != NULL)
+                       return (child);
+       }
+       return (NULL);
+}
+
+static void
+mode_tree_free_item(struct mode_tree_item *mti)
+{
+       mode_tree_free_items(&mti->children);
+
+       free((void *)mti->name);
+       free((void *)mti->text);
+
+       free(mti);
+}
+
+static void
+mode_tree_free_items(struct mode_tree_list *mtl)
+{
+       struct mode_tree_item   *mti, *mti1;
+
+       TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) {
+               TAILQ_REMOVE(mtl, mti, entry);
+               mode_tree_free_item(mti);
+       }
+}
+
+static void
+mode_tree_clear_lines(struct mode_tree_data *mtd)
+{
+       free(mtd->line_list);
+       mtd->line_list = NULL;
+       mtd->line_size = 0;
+}
+
+static void
+mode_tree_build_lines(struct mode_tree_data *mtd,
+    struct mode_tree_list *mtl, u_int depth)
+{
+       struct mode_tree_item   *mti;
+       struct mode_tree_line   *line;
+       u_int                    i;
+       int                      flat = 1;
+
+       mtd->depth = depth;
+       TAILQ_FOREACH(mti, mtl, entry) {
+               mtd->line_list = xreallocarray(mtd->line_list,
+                   mtd->line_size + 1, sizeof *mtd->line_list);
+
+               line = &mtd->line_list[mtd->line_size++];
+               line->item = mti;
+               line->depth = depth;
+               line->last = (mti == TAILQ_LAST(mtl, mode_tree_list));
+
+               mti->line = (mtd->line_size - 1);
+               if (!TAILQ_EMPTY(&mti->children))
+                       flat = 0;
+               if (mti->expanded)
+                       mode_tree_build_lines(mtd, &mti->children, depth + 1);
+       }
+       TAILQ_FOREACH(mti, mtl, entry) {
+               for (i = 0; i < mtd->line_size; i++) {
+                       line = &mtd->line_list[i];
+                       if (line->item == mti)
+                               line->flat = flat;
+               }
+       }
+}
+
+static void
+mode_tree_clear_tagged(struct mode_tree_list *mtl)
+{
+       struct mode_tree_item   *mti;
+
+       TAILQ_FOREACH(mti, mtl, entry) {
+               mti->tagged = 0;
+               mode_tree_clear_tagged(&mti->children);
+       }
+}
+
+void
+mode_tree_up(struct mode_tree_data *mtd, int wrap)
+{
+       if (mtd->current == 0) {
+               if (wrap) {
+                       mtd->current = mtd->line_size - 1;
+                       if (mtd->line_size >= mtd->height)
+                               mtd->offset = mtd->line_size - mtd->height;
+               }
+       } else {
+               mtd->current--;
+               if (mtd->current < mtd->offset)
+                       mtd->offset--;
+       }
+}
+
+void
+mode_tree_down(struct mode_tree_data *mtd, int wrap)
+{
+       if (mtd->current == mtd->line_size - 1) {
+               if (wrap) {
+                       mtd->current = 0;
+                       mtd->offset = 0;
+               }
+       } else {
+               mtd->current++;
+               if (mtd->current > mtd->offset + mtd->height - 1)
+                       mtd->offset++;
+       }
+}
+
+void *
+mode_tree_get_current(struct mode_tree_data *mtd)
+{
+       return (mtd->line_list[mtd->current].item->itemdata);
+}
+
+u_int
+mode_tree_count_tagged(struct mode_tree_data *mtd)
+{
+       struct mode_tree_item   *mti;
+       u_int                    i, tagged;
+
+       tagged = 0;
+       for (i = 0; i < mtd->line_size; i++) {
+               mti = mtd->line_list[i].item;
+               if (mti->tagged)
+                       tagged++;
+       }
+       return (tagged);
+}
+
+void
+mode_tree_each_tagged(struct mode_tree_data *mtd, void (*cb)(void *, void *,
+    key_code), key_code key, int current)
+{
+       struct mode_tree_item   *mti;
+       u_int                    i;
+       int                      fired;
+
+       fired = 0;
+       for (i = 0; i < mtd->line_size; i++) {
+               mti = mtd->line_list[i].item;
+               if (mti->tagged) {
+                       fired = 1;
+                       cb(mtd->modedata, mti->itemdata, key);
+               }
+       }
+       if (!fired && current) {
+               mti = mtd->line_list[mtd->current].item;
+               cb(mtd->modedata, mti->itemdata, key);
+       }
+}
+
+struct mode_tree_data *
+mode_tree_start(struct window_pane *wp, void (*buildcb)(void *, u_int,
+    uint64_t *), struct screen *(*drawcb)(void *, void *, u_int, u_int),
+    void *modedata, const char **sort_list, u_int sort_size, struct screen **s)
+{
+       struct mode_tree_data   *mtd;
+
+       mtd = xcalloc(1, sizeof *mtd);
+       mtd->wp = wp;
+       mtd->modedata = modedata;
+
+       mtd->sort_list = sort_list;
+       mtd->sort_size = sort_size;
+       mtd->sort_type = 0;
+
+       mtd->buildcb = buildcb;
+       mtd->drawcb = drawcb;
+
+       TAILQ_INIT(&mtd->children);
+
+       *s = &mtd->screen;
+       screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
+       (*s)->mode &= ~MODE_CURSOR;
+
+       return (mtd);
+}
+
+void
+mode_tree_build(struct mode_tree_data *mtd)
+{
+       struct screen   *s = &mtd->screen;
+       uint64_t         tag;
+       u_int            i;
+
+       if (mtd->line_list != NULL)
+               tag = mtd->line_list[mtd->current].item->tag;
+       else
+               tag = 0;
+
+       TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
+       TAILQ_INIT(&mtd->children);
+
+       mtd->buildcb(mtd->modedata, mtd->sort_type, &tag);
+
+       mode_tree_free_items(&mtd->saved);
+       TAILQ_INIT(&mtd->saved);
+
+       mode_tree_clear_lines(mtd);
+       mode_tree_build_lines(mtd, &mtd->children, 0);
+
+       for (i = 0; i < mtd->line_size; i++) {
+               if (mtd->line_list[i].item->tag == tag)
+                       break;
+       }
+       if (i != mtd->line_size)
+               mtd->current = i;
+       else {
+               mtd->current = 0;
+               mtd->offset = 0;
+       }
+
+       mtd->width = screen_size_x(s);
+       mtd->height = (screen_size_y(s) / 3) * 2;
+       if (mtd->height > mtd->line_size)
+               mtd->height = screen_size_y(s) / 2;
+       if (mtd->height < 10)
+               mtd->height = screen_size_y(s);
+       if (screen_size_y(s) - mtd->height < 2)
+               mtd->height = screen_size_y(s);
+}
+
+void
+mode_tree_free(struct mode_tree_data *mtd)
+{
+       mode_tree_free_items(&mtd->children);
+       mode_tree_clear_lines(mtd);
+       screen_free(&mtd->screen);
+       free(mtd);
+}
+
+void
+mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
+{
+       struct screen   *s = &mtd->screen;
+
+       screen_resize(s, sx, sy, 0);
+
+       mode_tree_build(mtd);
+       mode_tree_draw(mtd);
+
+       mtd->wp->flags |= PANE_REDRAW;
+}
+
+struct mode_tree_item *
+mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
+    void *itemdata, uint64_t tag, const char *name, const char *text,
+    int expanded)
+{
+       struct mode_tree_item   *mti, *saved;
+
+       log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
+           name, text);
+
+       mti = xcalloc(1, sizeof *mti);
+       mti->parent = parent;
+       mti->itemdata = itemdata;
+
+       mti->tag = tag;
+       mti->name = xstrdup(name);
+       mti->text = xstrdup(text);
+
+       saved = mode_tree_find_item(&mtd->saved, tag);
+       if (saved != NULL) {
+               if (parent == NULL || (parent != NULL && parent->expanded))
+                       mti->tagged = saved->tagged;
+               mti->expanded = saved->expanded;
+       } else if (expanded == -1)
+               mti->expanded = 1;
+       else
+               mti->expanded = expanded;
+
+       TAILQ_INIT(&mti->children);
+
+       if (parent != NULL)
+               TAILQ_INSERT_TAIL(&parent->children, mti, entry);
+       else
+               TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
+
+       return (mti);
+}
+
+void
+mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
+{
+       struct mode_tree_item   *parent = mti->parent;
+
+       if (parent != NULL)
+               TAILQ_REMOVE(&parent->children, mti, entry);
+       else
+               TAILQ_REMOVE(&mtd->children, mti, entry);
+       mode_tree_free_item(mti);
+}
+
+void
+mode_tree_draw(struct mode_tree_data *mtd)
+{
+       struct window_pane      *wp = mtd->wp;
+       struct screen           *s = &mtd->screen, *box;
+       struct mode_tree_line   *line;
+       struct mode_tree_item   *mti;
+       struct options          *oo = wp->window->options;
+       struct screen_write_ctx  ctx;
+       struct grid_cell         gc0, gc;
+       u_int                    w, h, i, j, sy, box_x, box_y;
+       char                    *text, *start, key[7];
+       const char              *tag, *symbol;
+       size_t                   size;
+       int                      keylen;
+
+       if (mtd->line_size == 0)
+               return;
+
+       memcpy(&gc0, &grid_default_cell, sizeof gc0);
+       memcpy(&gc, &grid_default_cell, sizeof gc);
+       style_apply(&gc, oo, "mode-style");
+
+       w = mtd->width;
+       h = mtd->height;
+
+       screen_write_start(&ctx, NULL, s);
+       screen_write_clearscreen(&ctx, 8);
+
+       if (mtd->line_size > 10)
+               keylen = 6;
+       else
+               keylen = 4;
+
+       for (i = 0; i < mtd->line_size; i++) {
+               if (i < mtd->offset)
+                       continue;
+               if (i > mtd->offset + h - 1)
+                       break;
+
+               line = &mtd->line_list[i];
+               mti = line->item;
+
+               screen_write_cursormove(&ctx, 0, i - mtd->offset);
+
+               if (i < 10)
+                       snprintf(key, sizeof key, "(%c)", '0' + i);
+               else if (i < 36)
+                       snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
+               else
+                       *key = '\0';
+
+               if (line->flat)
+                       symbol = "";
+               else if (TAILQ_EMPTY(&mti->children))
+                       symbol = "  ";
+               else if (mti->expanded)
+                       symbol = "- ";
+               else
+                       symbol = "+ ";
+
+               if (line->depth == 0)
+                       start = xstrdup(symbol);
+               else {
+                       size = (4 * line->depth) + 32;
+
+                       start = xcalloc(1, size);
+                       for (j = 1; j < line->depth; j++) {
+                               if (mti->parent != NULL &&
+                                   mtd->line_list[mti->parent->line].last)
+                                       strlcat(start, "    ", size);
+                               else
+                                       strlcat(start, "\001x\001   ", size);
+                       }
+                       if (line->last)
+                               strlcat(start, "\001mq\001> ", size);
+                       else
+                               strlcat(start, "\001tq\001> ", size);
+                       strlcat(start, symbol, size);
+               }
+
+               if (mti->tagged)
+                       tag = "*";
+               else
+                       tag = "";
+               xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start,
+                   mti->name, tag, mti->text);
+               free(start);
+
+               if (mti->tagged) {
+                       gc.attr ^= GRID_ATTR_BRIGHT;
+                       gc0.attr ^= GRID_ATTR_BRIGHT;
+               }
+
+               if (i != mtd->current) {
+                       screen_write_puts(&ctx, &gc0, "%.*s", w, text);
+                       screen_write_clearendofline(&ctx, 8);
+               } else
+                       screen_write_puts(&ctx, &gc, "%-*.*s", w, w, text);
+               free(text);
+
+               if (mti->tagged) {
+                       gc.attr ^= GRID_ATTR_BRIGHT;
+                       gc0.attr ^= GRID_ATTR_BRIGHT;
+               }
+       }
+
+       sy = screen_size_y(s);
+       if (sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
+               screen_write_stop(&ctx);
+               return;
+       }
+
+       line = &mtd->line_list[mtd->current];
+       mti = line->item;
+
+       screen_write_cursormove(&ctx, 0, h);
+       screen_write_box(&ctx, w, sy - h);
+
+       xasprintf(&text, " %s (sort: %s) ", mti->name,
+           mtd->sort_list[mtd->sort_type]);
+       if (w - 2 >= strlen(text)) {
+               screen_write_cursormove(&ctx, 1, h);
+               screen_write_puts(&ctx, &gc0, "%s", text);
+       }
+       free(text);
+
+       box_x = w - 4;
+       box_y = sy - h - 2;
+
+       box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y);
+       if (box != NULL) {
+               screen_write_cursormove(&ctx, 2, h + 1);
+               screen_write_copy(&ctx, box, 0, 0, box_x, box_y, NULL, NULL);
+
+               screen_free(box);
+       }
+
+       screen_write_stop(&ctx);
+}
+
+int
+mode_tree_key(struct mode_tree_data *mtd, key_code *key, struct mouse_event *m)
+{
+       struct mode_tree_line   *line;
+       struct mode_tree_item   *current, *parent;
+       u_int                    i, x, y;
+       int                      choice;
+       key_code                 tmp;
+
+       if (*key == KEYC_MOUSEDOWN1_PANE) {
+               if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
+                       *key = KEYC_NONE;
+                       return (0);
+               }
+               if (x > mtd->width || y > mtd->height) {
+                       *key = KEYC_NONE;
+                       return (0);
+               }
+               if (mtd->offset + y < mtd->line_size) {
+                       mtd->current = mtd->offset + y;
+                       *key = '\r';
+                       return (0);
+               }
+       }
+
+       line = &mtd->line_list[mtd->current];
+       current = line->item;
+
+       choice = -1;
+       if (*key >= '0' && *key <= '9')
+               choice = (*key) - '0';
+       else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
+               tmp = (*key) & KEYC_MASK_KEY;
+               if (tmp >= 'a' && tmp <= 'z')
+                       choice = 10 + (tmp - 'a');
+       }
+       if (choice != -1) {
+               if ((u_int)choice > mtd->line_size - 1) {
+                       *key = KEYC_NONE;
+                       return (0);
+               }
+               mtd->current = choice;
+               *key = '\r';
+               return (0);
+       }
+
+       switch (*key) {
+       case 'q':
+       case '\033': /* Escape */
+               return (1);
+       case KEYC_UP:
+       case 'k':
+       case KEYC_WHEELUP_PANE:
+               mode_tree_up(mtd, 1);
+               break;
+       case KEYC_DOWN:
+       case 'j':
+       case KEYC_WHEELDOWN_PANE:
+               mode_tree_down(mtd, 1);
+               break;
+       case KEYC_PPAGE:
+       case '\002': /* C-b */
+               for (i = 0; i < mtd->height; i++) {
+                       if (mtd->current == 0)
+                               break;
+                       mode_tree_up(mtd, 1);
+               }
+               break;
+       case KEYC_NPAGE:
+       case '\006': /* C-f */
+               for (i = 0; i < mtd->height; i++) {
+                       if (mtd->current == mtd->line_size - 1)
+                               break;
+                       mode_tree_down(mtd, 1);
+               }
+               break;
+       case KEYC_HOME:
+               mtd->current = 0;
+               mtd->offset = 0;
+               break;
+       case KEYC_END:
+               mtd->current = mtd->line_size - 1;
+               if (mtd->current > mtd->height - 1)
+                       mtd->offset = mtd->current - mtd->height;
+               else
+                       mtd->offset = 0;
+               break;
+       case 't':
+               /*
+                * Do not allow parents and children to both be tagged: untag
+                * all parents and children of current.
+                */
+               if (!current->tagged) {
+                       parent = current->parent;
+                       while (parent != NULL) {
+                               parent->tagged = 0;
+                               parent = parent->parent;
+                       }
+                       mode_tree_clear_tagged(&current->children);
+                       current->tagged = 1;
+               } else
+                       current->tagged = 0;
+               mode_tree_down(mtd, 0);
+               break;
+       case 'T':
+               for (i = 0; i < mtd->line_size; i++)
+                       mtd->line_list[i].item->tagged = 0;
+               break;
+       case '\024': /* C-t */
+               for (i = 0; i < mtd->line_size; i++) {
+                       if (mtd->line_list[i].item->parent == NULL)
+                               mtd->line_list[i].item->tagged = 1;
+                       else
+                               mtd->line_list[i].item->tagged = 0;
+               }
+               break;
+       case 'O':
+               mtd->sort_type++;
+               if (mtd->sort_type == mtd->sort_size)
+                       mtd->sort_type = 0;
+               mode_tree_build(mtd);
+               break;
+       case KEYC_LEFT:
+       case '-':
+               if (line->flat || !current->expanded)
+                       current = current->parent;
+               if (current == NULL)
+                       mode_tree_up(mtd, 0);
+               else {
+                       current->expanded = 0;
+                       mtd->current = current->line;
+                       mode_tree_build(mtd);
+               }
+               break;
+       case KEYC_RIGHT:
+       case '+':
+               if (line->flat || current->expanded)
+                       mode_tree_down(mtd, 0);
+               else if (!line->flat) {
+                       current->expanded = 1;
+                       mode_tree_build(mtd);
+               }
+               break;
+       }
+       return (0);
+}
+
+void
+mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
+    const char *template, const char *name)
+{
+       struct cmdq_item        *new_item;
+       struct cmd_list         *cmdlist;
+       char                    *command, *cause;
+
+       command = cmd_template_replace(template, name, 1);
+       if (command == NULL || *command == '\0')
+               return;
+
+       cmdlist = cmd_string_parse(command, NULL, 0, &cause);
+       if (cmdlist == NULL) {
+               if (cause != NULL && c != NULL) {
+                       *cause = toupper((u_char)*cause);
+                       status_message_set(c, "%s", cause);
+               }
+               free(cause);
+       } else {
+               new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
+               cmdq_append(c, new_item);
+               cmd_list_free(cmdlist);
+       }
+
+       free(command);
+}
index aaecc90..798d337 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: options-table.c,v 1.87 2017/05/29 20:41:29 nicm Exp $ */
+/* $OpenBSD: options-table.c,v 1.88 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -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 = ","
        },
 
index bb381f2..63f9985 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: screen-write.c,v 1.124 2017/05/12 14:56:56 nicm Exp $ */
+/* $OpenBSD: screen-write.c,v 1.125 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -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)
index d6cba36..ebfcf6b 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tmux.1,v 1.554 2017/05/29 18:06:34 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.555 2017/05/30 21:44:59 nicm Exp $
 .\"
 .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
 .\"
@@ -14,7 +14,7 @@
 .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: May 29 2017 $
+.Dd $Mdocdate: May 30 2017 $
 .Dt TMUX 1
 .Os
 .Sh NAME
@@ -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,
-.Ql %%
-is replaced by the session name 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
+starts with sessions collapsed and
 .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.
+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
-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,
+After a session, window or pane is chosen,
 .Ql %%
-is replaced by the session name and window index in
+is replaced by the target 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.
+is not given, "switch-client -t '%%'" is used.
+.Pp
 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 )
index 11e0ff5..a4c11b6 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.774 2017/05/29 20:42:53 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.775 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -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/usr.bin/tmux/window-buffer.c b/usr.bin/tmux/window-buffer.c
new file mode 100644 (file)
index 0000000..1f534ef
--- /dev/null
@@ -0,0 +1,342 @@
+/* $OpenBSD: window-buffer.c,v 1.1 2017/05/30 21:44:59 nicm Exp $ */
+
+/*
+ * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <vis.h>
+
+#include "tmux.h"
+
+static struct screen   *window_buffer_init(struct window_pane *,
+                            struct cmd_find_state *, struct args *);
+static void             window_buffer_free(struct window_pane *);
+static void             window_buffer_resize(struct window_pane *, u_int,
+                            u_int);
+static void             window_buffer_key(struct window_pane *,
+                            struct client *, struct session *, key_code,
+                            struct mouse_event *);
+
+#define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -b '%%'"
+
+const struct window_mode window_buffer_mode = {
+       .name = "buffer-mode",
+
+       .init = window_buffer_init,
+       .free = window_buffer_free,
+       .resize = window_buffer_resize,
+       .key = window_buffer_key,
+};
+
+enum window_buffer_sort_type {
+       WINDOW_BUFFER_BY_NAME,
+       WINDOW_BUFFER_BY_TIME,
+       WINDOW_BUFFER_BY_SIZE,
+};
+static const char *window_buffer_sort_list[] = {
+       "name",
+       "time",
+       "size"
+};
+
+struct window_buffer_itemdata {
+       const char      *name;
+       time_t           created;
+       u_int            order;
+       size_t           size;
+};
+
+struct window_buffer_modedata {
+       struct mode_tree_data            *data;
+       char                             *command;
+
+       struct window_buffer_itemdata   **item_list;
+       u_int                             item_size;
+};
+
+static struct window_buffer_itemdata *
+window_buffer_add_item(struct window_buffer_modedata *data)
+{
+       struct window_buffer_itemdata   *item;
+
+       data->item_list = xreallocarray(data->item_list, data->item_size + 1,
+           sizeof *data->item_list);
+       item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
+       return (item);
+}
+
+static void
+window_buffer_free_item(struct window_buffer_itemdata *item)
+{
+       free((void *)item->name);
+       free(item);
+}
+
+static int
+window_buffer_cmp_name(const void *a0, const void *b0)
+{
+       const struct window_buffer_itemdata *const *a = a0;
+       const struct window_buffer_itemdata *const *b = b0;
+
+       return (strcmp((*a)->name, (*b)->name));
+}
+
+static int
+window_buffer_cmp_time(const void *a0, const void *b0)
+{
+       const struct window_buffer_itemdata *const *a = a0;
+       const struct window_buffer_itemdata *const *b = b0;
+
+       if ((*a)->order > (*b)->order)
+               return (-1);
+       if ((*a)->order < (*b)->order)
+               return (1);
+       return (strcmp((*a)->name, (*b)->name));
+}
+
+static int
+window_buffer_cmp_size(const void *a0, const void *b0)
+{
+       const struct window_buffer_itemdata *const *a = a0;
+       const struct window_buffer_itemdata *const *b = b0;
+
+       if ((*a)->size > (*b)->size)
+               return (-1);
+       if ((*a)->size < (*b)->size)
+               return (1);
+       return (strcmp((*a)->name, (*b)->name));
+}
+
+static void
+window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag)
+{
+       struct window_buffer_modedata   *data = modedata;
+       struct window_buffer_itemdata   *item;
+       u_int                            i;
+       struct paste_buffer             *pb;
+       char                            *tim;
+       char                            *text;
+
+       for (i = 0; i < data->item_size; i++)
+               window_buffer_free_item(data->item_list[i]);
+       free(data->item_list);
+       data->item_list = NULL;
+       data->item_size = 0;
+
+       pb = NULL;
+       while ((pb = paste_walk(pb)) != NULL) {
+               item = window_buffer_add_item(data);
+               item->name = xstrdup(paste_buffer_name(pb));
+               item->created = paste_buffer_created(pb);
+               paste_buffer_data(pb, &item->size);
+               item->order = paste_buffer_order(pb);
+       }
+
+       switch (sort_type) {
+       case WINDOW_BUFFER_BY_NAME:
+               qsort(data->item_list, data->item_size, sizeof *data->item_list,
+                   window_buffer_cmp_name);
+               break;
+       case WINDOW_BUFFER_BY_TIME:
+               qsort(data->item_list, data->item_size, sizeof *data->item_list,
+                   window_buffer_cmp_time);
+               break;
+       case WINDOW_BUFFER_BY_SIZE:
+               qsort(data->item_list, data->item_size, sizeof *data->item_list,
+                   window_buffer_cmp_size);
+               break;
+       }
+
+       for (i = 0; i < data->item_size; i++) {
+               item = data->item_list[i];
+
+               tim = ctime(&item->created);
+               *strchr(tim, '\n') = '\0';
+
+               xasprintf(&text, "%zu bytes (%s)", item->size, tim);
+               mode_tree_add(data->data, NULL, item, item->order, item->name,
+                   text, -1);
+               free(text);
+       }
+
+}
+
+static struct screen *
+window_buffer_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
+{
+       struct window_buffer_itemdata   *item = itemdata;
+       struct paste_buffer             *pb;
+       static struct screen             s;
+       struct screen_write_ctx          ctx;
+       char                             line[1024];
+       const char                      *pdata, *end, *cp;
+       size_t                           psize, at;
+       u_int                            i;
+
+       pb = paste_get_name(item->name);
+       if (pb == NULL)
+               return (NULL);
+
+       screen_init(&s, sx, sy, 0);
+
+       screen_write_start(&ctx, NULL, &s);
+       screen_write_clearscreen(&ctx, 8);
+
+       pdata = end = paste_buffer_data (pb, &psize);
+       for (i = 0; i < sy; i++) {
+               at = 0;
+               while (end != pdata + psize && *end != '\n') {
+                       if ((sizeof line) - at > 5) {
+                               cp = vis(line + at, *end, VIS_TAB|VIS_OCTAL, 0);
+                               at = cp - line;
+                       }
+                       end++;
+               }
+               if (at > sx)
+                       at = sx;
+               line[at] = '\0';
+
+               if (*line != '\0') {
+                       screen_write_cursormove(&ctx, 0, i);
+                       screen_write_puts(&ctx, &grid_default_cell, "%s", line);
+               }
+
+               if (end == pdata + psize)
+                       break;
+               end++;
+       }
+
+       screen_write_stop(&ctx);
+       return (&s);
+}
+
+static struct screen *
+window_buffer_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
+    struct args *args)
+{
+       struct window_buffer_modedata   *data;
+       struct screen                   *s;
+
+       wp->modedata = data = xcalloc(1, sizeof *data);
+
+       if (args == NULL || args->argc == 0)
+               data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND);
+       else
+               data->command = xstrdup(args->argv[0]);
+
+       data->data = mode_tree_start(wp, window_buffer_build,
+           window_buffer_draw, data, window_buffer_sort_list,
+           nitems(window_buffer_sort_list), &s);
+
+       mode_tree_build(data->data);
+       mode_tree_draw(data->data);
+
+       return (s);
+}
+
+static void
+window_buffer_free(struct window_pane *wp)
+{
+       struct window_buffer_modedata   *data = wp->modedata;
+       u_int                            i;
+
+       if (data == NULL)
+               return;
+
+       mode_tree_free(data->data);
+
+       for (i = 0; i < data->item_size; i++)
+               window_buffer_free_item(data->item_list[i]);
+       free(data->item_list);
+
+       free(data->command);
+       free(data);
+}
+
+static void
+window_buffer_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+       struct window_buffer_modedata   *data = wp->modedata;
+
+       mode_tree_resize(data->data, sx, sy);
+}
+
+static void
+window_buffer_do_delete(void* modedata, void *itemdata, __unused key_code key)
+{
+       struct window_buffer_modedata   *data = modedata;
+       struct window_buffer_itemdata   *item = itemdata;
+       struct paste_buffer             *pb;
+
+       if (item == mode_tree_get_current(data->data))
+               mode_tree_down(data->data, 0);
+       if ((pb = paste_get_name(item->name)) != NULL)
+               paste_free(pb);
+}
+
+static void
+window_buffer_key(struct window_pane *wp, struct client *c,
+    __unused struct session *s, key_code key, struct mouse_event *m)
+{
+       struct window_buffer_modedata   *data = wp->modedata;
+       struct window_buffer_itemdata   *item;
+       char                            *command, *name;
+       int                              finished;
+
+       /*
+        * t = toggle tag
+        * T = tag none
+        * C-t = tag all
+        * q = exit
+        * O = change sort order
+        *
+        * d = delete buffer
+        * D = delete tagged buffers
+        * Enter = paste buffer
+        */
+
+       finished = mode_tree_key(data->data, &key, m);
+       switch (key) {
+       case 'd':
+               item = mode_tree_get_current(data->data);
+               window_buffer_do_delete(data, item, key);
+               mode_tree_build(data->data);
+               break;
+       case 'D':
+               mode_tree_each_tagged(data->data, window_buffer_do_delete, key,
+                   0);
+               mode_tree_build(data->data);
+               break;
+       case '\r':
+               item = mode_tree_get_current(data->data);
+               command = xstrdup(data->command);
+               name = xstrdup(item->name);
+               window_pane_reset_mode(wp);
+               mode_tree_run_command(c, NULL, command, name);
+               free(name);
+               free(command);
+               return;
+       }
+       if (finished || paste_get_top(NULL) == NULL)
+               window_pane_reset_mode(wp);
+       else {
+               mode_tree_draw(data->data);
+               wp->flags |= PANE_REDRAW;
+       }
+}
diff --git a/usr.bin/tmux/window-choose.c b/usr.bin/tmux/window-choose.c
deleted file mode 100644 (file)
index 09aebf8..0000000
+++ /dev/null
@@ -1,1078 +0,0 @@
-/* $OpenBSD: window-choose.c,v 1.91 2017/05/07 22:27:57 nicm Exp $ */
-
-/*
- * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-
-#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[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[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/usr.bin/tmux/window-client.c b/usr.bin/tmux/window-client.c
new file mode 100644 (file)
index 0000000..0411c56
--- /dev/null
@@ -0,0 +1,335 @@
+/* $OpenBSD: window-client.c,v 1.1 2017/05/30 21:44:59 nicm Exp $ */
+
+/*
+ * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+static struct screen   *window_client_init(struct window_pane *,
+                            struct cmd_find_state *, struct args *);
+static void             window_client_free(struct window_pane *);
+static void             window_client_resize(struct window_pane *, u_int,
+                            u_int);
+static void             window_client_key(struct window_pane *,
+                            struct client *, struct session *, key_code,
+                            struct mouse_event *);
+
+#define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'"
+
+const struct window_mode window_client_mode = {
+       .name = "client-mode",
+
+       .init = window_client_init,
+       .free = window_client_free,
+       .resize = window_client_resize,
+       .key = window_client_key,
+};
+
+enum window_client_sort_type {
+       WINDOW_CLIENT_BY_NAME,
+       WINDOW_CLIENT_BY_CREATION_TIME,
+       WINDOW_CLIENT_BY_ACTIVITY_TIME,
+};
+static const char *window_client_sort_list[] = {
+       "name",
+       "creation time",
+       "activity time"
+};
+
+struct window_client_itemdata {
+       struct client   *c;
+};
+
+struct window_client_modedata {
+       struct mode_tree_data            *data;
+       char                             *command;
+
+       struct window_client_itemdata   **item_list;
+       u_int                             item_size;
+};
+
+static struct window_client_itemdata *
+window_client_add_item(struct window_client_modedata *data)
+{
+       struct window_client_itemdata   *item;
+
+       data->item_list = xreallocarray(data->item_list, data->item_size + 1,
+           sizeof *data->item_list);
+       item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
+       return (item);
+}
+
+static void
+window_client_free_item(struct window_client_itemdata *item)
+{
+       server_client_unref(item->c);
+       free(item);
+}
+
+static int
+window_client_cmp_name(const void *a0, const void *b0)
+{
+       const struct window_client_itemdata *const *a = a0;
+       const struct window_client_itemdata *const *b = b0;
+
+       return (strcmp((*a)->c->name, (*b)->c->name));
+}
+
+static int
+window_client_cmp_creation_time(const void *a0, const void *b0)
+{
+       const struct window_client_itemdata *const *a = a0;
+       const struct window_client_itemdata *const *b = b0;
+
+       if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, >))
+               return (-1);
+       if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, <))
+               return (1);
+       return (0);
+}
+
+static int
+window_client_cmp_activity_time(const void *a0, const void *b0)
+{
+       const struct window_client_itemdata *const *a = a0;
+       const struct window_client_itemdata *const *b = b0;
+
+       if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, >))
+               return (-1);
+       if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, <))
+               return (1);
+       return (0);
+}
+
+static void
+window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag)
+{
+       struct window_client_modedata   *data = modedata;
+       struct window_client_itemdata   *item;
+       u_int                            i;
+       struct client                   *c;
+       char                            *tim;
+       char                            *text;
+
+       for (i = 0; i < data->item_size; i++)
+               window_client_free_item(data->item_list[i]);
+       free(data->item_list);
+       data->item_list = NULL;
+       data->item_size = 0;
+
+       TAILQ_FOREACH(c, &clients, entry) {
+               if (c->session == NULL || (c->flags & (CLIENT_DETACHING)))
+                       continue;
+
+               item = window_client_add_item(data);
+               item->c = c;
+
+               c->references++;
+       }
+
+       switch (sort_type) {
+       case WINDOW_CLIENT_BY_NAME:
+               qsort(data->item_list, data->item_size, sizeof *data->item_list,
+                   window_client_cmp_name);
+               break;
+       case WINDOW_CLIENT_BY_CREATION_TIME:
+               qsort(data->item_list, data->item_size, sizeof *data->item_list,
+                   window_client_cmp_creation_time);
+               break;
+       case WINDOW_CLIENT_BY_ACTIVITY_TIME:
+               qsort(data->item_list, data->item_size, sizeof *data->item_list,
+                   window_client_cmp_activity_time);
+               break;
+       }
+
+       for (i = 0; i < data->item_size; i++) {
+               item = data->item_list[i];
+               c = item->c;
+
+               tim = ctime(&c->activity_time.tv_sec);
+               *strchr(tim, '\n') = '\0';
+
+               xasprintf(&text, "session %s (%s)", c->session->name, tim);
+               mode_tree_add(data->data, NULL, item, (uint64_t)c, c->name,
+                   text, -1);
+               free(text);
+       }
+}
+
+static struct screen *
+window_client_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
+{
+       struct window_client_itemdata   *item = itemdata;
+       struct client                   *c = item->c;
+       struct window_pane              *wp;
+       static struct screen             s;
+       struct screen_write_ctx          ctx;
+
+       if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING)))
+               return (NULL);
+       wp = c->session->curw->window->active;
+
+       screen_init(&s, sx, sy, 0);
+
+       screen_write_start(&ctx, NULL, &s);
+       screen_write_clearscreen(&ctx, 8);
+
+       screen_write_preview(&ctx, &wp->base, sx, sy - 3);
+
+       screen_write_cursormove(&ctx, 0, sy - 2);
+       screen_write_line(&ctx, sx, 0, 0);
+
+       screen_write_cursormove(&ctx, 0, sy - 1);
+       if (c->old_status != NULL)
+               screen_write_copy(&ctx, c->old_status, 0, 0, sx, 1, NULL, NULL);
+       else
+               screen_write_copy(&ctx, &c->status, 0, 0, sx, 1, NULL, NULL);
+
+       screen_write_stop(&ctx);
+       return (&s);
+}
+
+static struct screen *
+window_client_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
+    struct args *args)
+{
+       struct window_client_modedata   *data;
+       struct screen                   *s;
+
+       wp->modedata = data = xcalloc(1, sizeof *data);
+
+       if (args == NULL || args->argc == 0)
+               data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND);
+       else
+               data->command = xstrdup(args->argv[0]);
+
+       data->data = mode_tree_start(wp, window_client_build,
+           window_client_draw, data, window_client_sort_list,
+           nitems(window_client_sort_list), &s);
+
+       mode_tree_build(data->data);
+       mode_tree_draw(data->data);
+
+       return (s);
+}
+
+static void
+window_client_free(struct window_pane *wp)
+{
+       struct window_client_modedata   *data = wp->modedata;
+       u_int                            i;
+
+       if (data == NULL)
+               return;
+
+       mode_tree_free(data->data);
+
+       for (i = 0; i < data->item_size; i++)
+               window_client_free_item(data->item_list[i]);
+       free(data->item_list);
+
+       free(data->command);
+       free(data);
+}
+
+static void
+window_client_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+       struct window_client_modedata   *data = wp->modedata;
+
+       mode_tree_resize(data->data, sx, sy);
+}
+
+static void
+window_client_do_detach(void* modedata, void *itemdata, key_code key)
+{
+       struct window_client_modedata   *data = modedata;
+       struct window_client_itemdata   *item = itemdata;
+
+       if (item == mode_tree_get_current(data->data))
+               mode_tree_down(data->data, 0);
+       if (key == 'd' || key == 'D')
+               server_client_detach(item->c, MSG_DETACH);
+       else if (key == 'x' || key == 'X')
+               server_client_detach(item->c, MSG_DETACHKILL);
+       else if (key == 'z' || key == 'Z')
+               server_client_suspend(item->c);
+}
+
+static void
+window_client_key(struct window_pane *wp, struct client *c,
+    __unused struct session *s, key_code key, struct mouse_event *m)
+{
+       struct window_client_modedata   *data = wp->modedata;
+       struct window_client_itemdata   *item;
+       char                            *command, *name;
+       int                              finished;
+
+       /*
+        * t = toggle tag
+        * T = tag none
+        * C-t = tag all
+        * q = exit
+        * O = change sort order
+        *
+        * d = detach client
+        * D = detach tagged clients
+        * x = detach and kill client
+        * X = detach and kill tagged clients
+        * z = suspend client
+        * Z = suspend tagged clients
+        * Enter = detach client
+        */
+
+       finished = mode_tree_key(data->data, &key, m);
+       switch (key) {
+       case 'd':
+       case 'x':
+       case 'z':
+               item = mode_tree_get_current(data->data);
+               window_client_do_detach(data, item, key);
+               mode_tree_build(data->data);
+               break;
+       case 'D':
+       case 'X':
+       case 'Z':
+               mode_tree_each_tagged(data->data, window_client_do_detach, key,
+                   0);
+               mode_tree_build(data->data);
+               break;
+       case '\r':
+               item = mode_tree_get_current(data->data);
+               command = xstrdup(data->command);
+               name = xstrdup(item->c->ttyname);
+               window_pane_reset_mode(wp);
+               mode_tree_run_command(c, NULL, command, name);
+               free(name);
+               free(command);
+               return;
+       }
+       if (finished || server_client_how_many() == 0)
+               window_pane_reset_mode(wp);
+       else {
+               mode_tree_draw(data->data);
+               wp->flags |= PANE_REDRAW;
+       }
+}
index 168cbd0..2c03968 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: window-clock.c,v 1.23 2017/05/07 22:27:57 nicm Exp $ */
+/* $OpenBSD: window-clock.c,v 1.24 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -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;
index e7bb44b..4a2aeed 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: window-copy.c,v 1.176 2017/05/29 07:58:33 nicm Exp $ */
+/* $OpenBSD: window-copy.c,v 1.177 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -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/usr.bin/tmux/window-tree.c b/usr.bin/tmux/window-tree.c
new file mode 100644 (file)
index 0000000..c0c1e90
--- /dev/null
@@ -0,0 +1,713 @@
+/* $OpenBSD: window-tree.c,v 1.1 2017/05/30 21:44:59 nicm Exp $ */
+
+/*
+ * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+static struct screen   *window_tree_init(struct window_pane *,
+                            struct cmd_find_state *, struct args *);
+static void             window_tree_free(struct window_pane *);
+static void             window_tree_resize(struct window_pane *, u_int, u_int);
+static void             window_tree_key(struct window_pane *,
+                            struct client *, struct session *, key_code,
+                            struct mouse_event *);
+
+#define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'"
+
+const struct window_mode window_tree_mode = {
+       .name = "tree-mode",
+
+       .init = window_tree_init,
+       .free = window_tree_free,
+       .resize = window_tree_resize,
+       .key = window_tree_key,
+};
+
+enum window_tree_sort_type {
+       WINDOW_TREE_BY_INDEX,
+       WINDOW_TREE_BY_NAME,
+       WINDOW_TREE_BY_TIME,
+};
+static const char *window_tree_sort_list[] = {
+       "index",
+       "name",
+       "time"
+};
+
+enum window_tree_type {
+       WINDOW_TREE_NONE,
+       WINDOW_TREE_SESSION,
+       WINDOW_TREE_WINDOW,
+       WINDOW_TREE_PANE,
+};
+
+struct window_tree_itemdata {
+       enum window_tree_type   type;
+       int                     session;
+       int                     winlink;
+       int                     pane;
+};
+
+struct window_tree_modedata {
+       struct window_pane               *wp;
+       int                               dead;
+       int                               references;
+
+       struct mode_tree_data            *data;
+       char                             *command;
+
+       struct window_tree_itemdata     **item_list;
+       u_int                             item_size;
+
+       struct client                    *client;
+       const char                       *entered;
+
+       char                             *filter;
+
+       struct cmd_find_state             fs;
+       enum window_tree_type             type;
+};
+
+static void
+window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
+    struct winlink **wlp, struct window_pane **wp)
+{
+       *wp = NULL;
+       *wlp = NULL;
+       *sp = session_find_by_id(item->session);
+       if (*sp == NULL)
+               return;
+       if (item->type == WINDOW_TREE_SESSION) {
+               *wlp = (*sp)->curw;
+               *wp = (*wlp)->window->active;
+               return;
+       }
+
+       *wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
+       if (*wlp == NULL) {
+               *sp = NULL;
+               return;
+       }
+       if (item->type == WINDOW_TREE_WINDOW) {
+               *wp = (*wlp)->window->active;
+               return;
+       }
+
+       *wp = window_pane_find_by_id(item->pane);
+       if (!window_has_pane((*wlp)->window, *wp))
+               *wp = NULL;
+       if (*wp == NULL) {
+               *sp = NULL;
+               *wlp = NULL;
+               return;
+       }
+}
+
+static struct window_tree_itemdata *
+window_tree_add_item(struct window_tree_modedata *data)
+{
+       struct window_tree_itemdata     *item;
+
+       data->item_list = xreallocarray(data->item_list, data->item_size + 1,
+           sizeof *data->item_list);
+       item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
+       return (item);
+}
+
+static void
+window_tree_free_item(struct window_tree_itemdata *item)
+{
+       free(item);
+}
+
+static int
+window_tree_cmp_session_name(const void *a0, const void *b0)
+{
+       const struct session *const *a = a0;
+       const struct session *const *b = b0;
+
+       return (strcmp((*a)->name, (*b)->name));
+}
+
+static int
+window_tree_cmp_session_time(const void *a0, const void *b0)
+{
+       const struct session *const *a = a0;
+       const struct session *const *b = b0;
+
+       if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >))
+               return (-1);
+       if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <))
+               return (1);
+       return (strcmp((*a)->name, (*b)->name));
+}
+
+static int
+window_tree_cmp_window_name(const void *a0, const void *b0)
+{
+       const struct winlink *const *a = a0;
+       const struct winlink *const *b = b0;
+
+       return (strcmp((*a)->window->name, (*b)->window->name));
+}
+
+static int
+window_tree_cmp_window_time(const void *a0, const void *b0)
+{
+       const struct winlink *const *a = a0;
+       const struct winlink *const *b = b0;
+
+       if (timercmp(&(*a)->window->activity_time,
+           &(*b)->window->activity_time, >))
+               return (-1);
+       if (timercmp(&(*a)->window->activity_time,
+           &(*b)->window->activity_time, <))
+               return (1);
+       return (strcmp((*a)->window->name, (*b)->window->name));
+}
+
+static int
+window_tree_cmp_pane_time(const void *a0, const void *b0)
+{
+       const struct window_pane *const *a = a0;
+       const struct window_pane *const *b = b0;
+
+       if ((*a)->active_point < (*b)->active_point)
+               return (-1);
+       if ((*a)->active_point > (*b)->active_point)
+               return (1);
+       return (0);
+}
+
+static void
+window_tree_build_pane(struct session *s, struct winlink *wl,
+    struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
+{
+       struct window_tree_modedata     *data = modedata;
+       struct window_tree_itemdata     *item;
+       char                            *name, *text;
+       u_int                            idx;
+
+       window_pane_index(wp, &idx);
+
+       item = window_tree_add_item(data);
+       item->type = WINDOW_TREE_PANE;
+       item->session = s->id;
+       item->winlink = wl->idx;
+       item->pane = wp->id;
+
+       text = format_single(NULL,
+           "#{pane_current_command} \"#{pane_title}\"",
+           NULL, s, wl, wp);
+       xasprintf(&name, "%u", idx);
+
+       mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1);
+       free(text);
+       free(name);
+}
+
+static int
+window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
+    u_int sort_type, struct mode_tree_item *parent, int no_filter)
+{
+       struct window_tree_modedata     *data = modedata;
+       struct window_tree_itemdata     *item;
+       struct mode_tree_item           *mti;
+       char                            *name, *text, *cp;
+       struct window_pane              *wp, **l;
+       u_int                            n, i;
+       int                              expanded;
+
+       item = window_tree_add_item(data);
+       item->type = WINDOW_TREE_WINDOW;
+       item->session = s->id;
+       item->winlink = wl->idx;
+       item->pane = -1;
+
+       text = format_single(NULL,
+           "#{window_name}#{window_flags} (#{window_panes} panes)",
+           NULL, s, wl, NULL);
+       xasprintf(&name, "%u", wl->idx);
+
+       if (data->type == WINDOW_TREE_SESSION ||
+           data->type == WINDOW_TREE_WINDOW)
+               expanded = 0;
+       else
+               expanded = 1;
+       mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text,
+           expanded);
+       free(text);
+       free(name);
+
+       l = NULL;
+       n = 0;
+       TAILQ_FOREACH(wp, &wl->window->panes, entry) {
+               if (!no_filter && data->filter != NULL) {
+                       cp = format_single(NULL, data->filter, NULL, s, wl, wp);
+                       if (!format_true(cp)) {
+                               free(cp);
+                               continue;
+                       }
+                       free(cp);
+               }
+               l = xreallocarray(l, n + 1, sizeof *l);
+               l[n++] = wp;
+       }
+       if (n == 0) {
+               window_tree_free_item(item);
+               data->item_size--;
+               mode_tree_remove(data->data, mti);
+               return (0);
+       }
+
+       switch (sort_type) {
+       case WINDOW_TREE_BY_INDEX:
+               break;
+       case WINDOW_TREE_BY_NAME:
+               /* Panes don't have names, so leave in number order. */
+               break;
+       case WINDOW_TREE_BY_TIME:
+               qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
+               break;
+       }
+
+       for (i = 0; i < n; i++)
+               window_tree_build_pane(s, wl, l[i], modedata, mti);
+       free(l);
+       return (1);
+}
+
+static void
+window_tree_build_session(struct session *s, void* modedata,
+    u_int sort_type, int no_filter)
+{
+       struct window_tree_modedata     *data = modedata;
+       struct window_tree_itemdata     *item;
+       struct mode_tree_item           *mti;
+       char                            *text;
+       struct winlink                  *wl, **l;
+       u_int                            n, i, empty;
+       int                              expanded;
+
+       item = window_tree_add_item(data);
+       item->type = WINDOW_TREE_SESSION;
+       item->session = s->id;
+       item->winlink = -1;
+       item->pane = -1;
+
+       text = format_single(NULL,
+           "#{session_windows} windows"
+           "#{?session_grouped, (group ,}"
+           "#{session_group}#{?session_grouped,),}"
+           "#{?session_attached, (attached),}",
+           NULL, s, NULL, NULL);
+
+       if (data->type == WINDOW_TREE_SESSION)
+               expanded = 0;
+       else
+               expanded = 1;
+       mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text,
+           expanded);
+       free(text);
+
+       l = NULL;
+       n = 0;
+       RB_FOREACH(wl, winlinks, &s->windows) {
+               l = xreallocarray(l, n + 1, sizeof *l);
+               l[n++] = wl;
+       }
+       switch (sort_type) {
+       case WINDOW_TREE_BY_INDEX:
+               break;
+       case WINDOW_TREE_BY_NAME:
+               qsort(l, n, sizeof *l, window_tree_cmp_window_name);
+               break;
+       case WINDOW_TREE_BY_TIME:
+               qsort(l, n, sizeof *l, window_tree_cmp_window_time);
+               break;
+       }
+
+       empty = 0;
+       for (i = 0; i < n; i++) {
+               if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
+                   no_filter))
+                       empty++;
+       }
+       if (empty == n) {
+               window_tree_free_item(item);
+               data->item_size--;
+               mode_tree_remove(data->data, mti);
+       }
+       free(l);
+}
+
+static void
+window_tree_build(void *modedata, u_int sort_type, uint64_t *tag)
+{
+       struct window_tree_modedata     *data = modedata;
+       struct session                  *s, **l;
+       u_int                            n, i;
+       int                              no_filter = 0;
+
+restart:
+       for (i = 0; i < data->item_size; i++)
+               window_tree_free_item(data->item_list[i]);
+       free(data->item_list);
+       data->item_list = NULL;
+       data->item_size = 0;
+
+       l = NULL;
+       n = 0;
+       RB_FOREACH(s, sessions, &sessions) {
+               l = xreallocarray(l, n + 1, sizeof *l);
+               l[n++] = s;
+       }
+       switch (sort_type) {
+       case WINDOW_TREE_BY_INDEX:
+               break;
+       case WINDOW_TREE_BY_NAME:
+               qsort(l, n, sizeof *l, window_tree_cmp_session_name);
+               break;
+       case WINDOW_TREE_BY_TIME:
+               qsort(l, n, sizeof *l, window_tree_cmp_session_time);
+               break;
+       }
+
+       for (i = 0; i < n; i++)
+               window_tree_build_session(l[i], modedata, sort_type, no_filter);
+       free(l);
+
+       if (!no_filter && data->item_size == 0) {
+               no_filter = 1;
+               goto restart;
+       }
+
+       switch (data->type) {
+       case WINDOW_TREE_NONE:
+               break;
+       case WINDOW_TREE_SESSION:
+               *tag = (uint64_t)data->fs.s;
+               break;
+       case WINDOW_TREE_WINDOW:
+               *tag = (uint64_t)data->fs.wl;
+               break;
+       case WINDOW_TREE_PANE:
+               *tag = (uint64_t)data->fs.wp;
+               break;
+       }
+}
+
+static struct screen *
+window_tree_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
+{
+       struct window_tree_itemdata     *item = itemdata;
+       struct session                  *sp;
+       struct winlink                  *wlp;
+       struct window_pane              *wp;
+       static struct screen             s;
+       struct screen_write_ctx          ctx;
+
+       window_tree_pull_item(item, &sp, &wlp, &wp);
+       if (wp == NULL)
+               return (NULL);
+
+       screen_init(&s, sx, sy, 0);
+
+       screen_write_start(&ctx, NULL, &s);
+
+       screen_write_preview(&ctx, &wp->base, sx, sy);
+
+       screen_write_stop(&ctx);
+       return (&s);
+}
+
+static struct screen *
+window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
+    struct args *args)
+{
+       struct window_tree_modedata     *data;
+       struct screen                   *s;
+
+       wp->modedata = data = xcalloc(1, sizeof *data);
+
+       if (args_has(args, 's'))
+               data->type = WINDOW_TREE_SESSION;
+       else if (args_has(args, 'w'))
+               data->type = WINDOW_TREE_WINDOW;
+       else
+               data->type = WINDOW_TREE_PANE;
+       memcpy(&data->fs, fs, sizeof data->fs);
+
+       data->wp = wp;
+       data->references = 1;
+
+       if (args_has(args, 'f'))
+               data->filter = xstrdup(args_get(args, 'f'));
+       else
+               data->filter = NULL;
+
+       if (args == NULL || args->argc == 0)
+               data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
+       else
+               data->command = xstrdup(args->argv[0]);
+
+       data->data = mode_tree_start(wp, window_tree_build,
+           window_tree_draw, data, window_tree_sort_list,
+           nitems(window_tree_sort_list), &s);
+
+       mode_tree_build(data->data);
+       mode_tree_draw(data->data);
+
+       data->type = WINDOW_TREE_NONE;
+
+       return (s);
+}
+
+static void
+window_tree_destroy(struct window_tree_modedata *data)
+{
+       u_int   i;
+
+       if (--data->references != 0)
+               return;
+
+       mode_tree_free(data->data);
+
+       for (i = 0; i < data->item_size; i++)
+               window_tree_free_item(data->item_list[i]);
+       free(data->item_list);
+
+       free(data->filter);
+
+       free(data->command);
+       free(data);
+}
+
+static void
+window_tree_free(struct window_pane *wp)
+{
+       struct window_tree_modedata *data = wp->modedata;
+
+       if (data == NULL)
+               return;
+
+       data->dead = 1;
+       window_tree_destroy(data);
+}
+
+static void
+window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+       struct window_tree_modedata     *data = wp->modedata;
+
+       mode_tree_resize(data->data, sx, sy);
+}
+
+static char *
+window_tree_get_target(struct window_tree_itemdata *item,
+    struct cmd_find_state *fs)
+{
+       struct session          *s;
+       struct winlink          *wl;
+       struct window_pane      *wp;
+       char                    *target;
+
+       window_tree_pull_item(item, &s, &wl, &wp);
+
+       target = NULL;
+       switch (item->type) {
+       case WINDOW_TREE_NONE:
+               break;
+       case WINDOW_TREE_SESSION:
+               if (s == NULL)
+                       break;
+               xasprintf(&target, "=%s:", s->name);
+               break;
+       case WINDOW_TREE_WINDOW:
+               if (s == NULL || wl == NULL)
+                       break;
+               xasprintf(&target, "=%s:%u.", s->name, wl->idx);
+               break;
+       case WINDOW_TREE_PANE:
+               if (s == NULL || wl == NULL || wp == NULL)
+                       break;
+               xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
+               break;
+       }
+       if (target == NULL)
+               cmd_find_clear_state(fs, 0);
+       else
+               cmd_find_from_winlink_pane(fs, wl, wp);
+       return (target);
+}
+
+static void
+window_tree_command_each(void* modedata, void* itemdata, __unused key_code key)
+{
+       struct window_tree_modedata     *data = modedata;
+       struct window_tree_itemdata     *item = itemdata;
+       char                            *name;
+       struct cmd_find_state            fs;
+
+       name = window_tree_get_target(item, &fs);
+       if (name != NULL)
+               mode_tree_run_command(data->client, &fs, data->entered, name);
+       free(name);
+}
+
+static enum cmd_retval
+window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
+{
+       struct window_tree_modedata     *data = modedata;
+
+       if (!data->dead) {
+               mode_tree_build(data->data);
+               mode_tree_draw(data->data);
+               data->wp->flags |= PANE_REDRAW;
+       }
+       window_tree_destroy(data);
+       return (CMD_RETURN_NORMAL);
+}
+
+static int
+window_tree_command_callback(struct client *c, void *modedata, const char *s,
+    __unused int done)
+{
+       struct window_tree_modedata     *data = modedata;
+
+       if (data->dead)
+               return (0);
+
+       data->client = c;
+       data->entered = s;
+
+       mode_tree_each_tagged(data->data, window_tree_command_each, KEYC_NONE,
+           1);
+
+       data->client = NULL;
+       data->entered = NULL;
+
+       data->references++;
+       cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
+
+       return (0);
+}
+
+static void
+window_tree_command_free(void *modedata)
+{
+       struct window_tree_modedata     *data = modedata;
+
+       window_tree_destroy(data);
+}
+
+static int
+window_tree_filter_callback(__unused struct client *c, void *modedata,
+    const char *s, __unused int done)
+{
+       struct window_tree_modedata     *data = modedata;
+
+       if (data->dead)
+               return (0);
+
+       if (data->filter != NULL)
+               free(data->filter);
+       if (s == NULL || *s == '\0')
+               data->filter = NULL;
+       else
+               data->filter = xstrdup(s);
+
+       mode_tree_build(data->data);
+       mode_tree_draw(data->data);
+       data->wp->flags |= PANE_REDRAW;
+
+       return (0);
+}
+
+static void
+window_tree_filter_free(void *modedata)
+{
+       struct window_tree_modedata     *data = modedata;
+
+       window_tree_destroy(data);
+}
+
+static void
+window_tree_key(struct window_pane *wp, struct client *c,
+    __unused struct session *s, key_code key, struct mouse_event *m)
+{
+       struct window_tree_modedata     *data = wp->modedata;
+       struct window_tree_itemdata     *item;
+       char                            *command, *name, *prompt;
+       struct cmd_find_state            fs;
+       int                              finished;
+       u_int                            tagged;
+
+       /*
+        * t = toggle tag
+        * T = tag none
+        * C-t = tag all
+        * q = exit
+        * O = change sort order
+        *
+        * Enter = select item
+        * : = enter command
+        * f = enter filter
+        */
+
+       finished = mode_tree_key(data->data, &key, m);
+       switch (key) {
+       case 'f':
+               data->references++;
+               status_prompt_set(c, "(filter) ", data->filter,
+                   window_tree_filter_callback, window_tree_filter_free, data,
+                   PROMPT_NOFORMAT);
+               break;
+       case ':':
+               tagged = mode_tree_count_tagged(data->data);
+               if (tagged != 0)
+                       xasprintf(&prompt, "(%u tagged) ", tagged);
+               else
+                       xasprintf(&prompt, "(current) ");
+               data->references++;
+               status_prompt_set(c, prompt, "", window_tree_command_callback,
+                   window_tree_command_free, data, PROMPT_NOFORMAT);
+               free(prompt);
+               break;
+       case '\r':
+               item = mode_tree_get_current(data->data);
+               command = xstrdup(data->command);
+               name = window_tree_get_target(item, &fs);
+               window_pane_reset_mode(wp);
+               if (name != NULL)
+                       mode_tree_run_command(c, &fs, command, name);
+               free(name);
+               free(command);
+               return;
+       }
+       if (finished)
+               window_pane_reset_mode(wp);
+       else {
+               mode_tree_draw(data->data);
+               wp->flags |= PANE_REDRAW;
+       }
+}
index 28110f0..49c8f26 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: window.c,v 1.195 2017/05/29 18:06:34 nicm Exp $ */
+/* $OpenBSD: window.c,v 1.196 2017/05/30 21:44:59 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -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)