xref: /openbsd-src/usr.bin/tmux/cmd-choose-tree.c (revision cb39b41371628601fbe4c618205356d538b9d08a)
1 /* $OpenBSD: cmd-choose-tree.c,v 1.28 2015/04/27 16:25:57 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <ctype.h>
22 #include <stdlib.h>
23 
24 #include <string.h>
25 
26 #include "tmux.h"
27 
28 #define CMD_CHOOSE_TREE_WINDOW_ACTION "select-window -t '%%'"
29 #define CMD_CHOOSE_TREE_SESSION_ACTION "switch-client -t '%%'"
30 
31 /*
32  * Enter choice mode to choose a session and/or window.
33  */
34 
35 #define CHOOSE_TREE_SESSION_TEMPLATE				\
36 	"#{session_name}: #{session_windows} windows"		\
37 	"#{?session_grouped, (group ,}"				\
38 	"#{session_group}#{?session_grouped,),}"		\
39 	"#{?session_attached, (attached),}"
40 #define CHOOSE_TREE_WINDOW_TEMPLATE				\
41 	"#{window_index}: #{window_name}#{window_flags} "	\
42 	"\"#{pane_title}\""
43 
44 enum cmd_retval	cmd_choose_tree_exec(struct cmd *, struct cmd_q *);
45 
46 const struct cmd_entry cmd_choose_tree_entry = {
47 	"choose-tree", NULL,
48 	"S:W:swub:c:t:", 0, 1,
49 	"[-suw] [-b session-template] [-c window template] [-S format] " \
50 	"[-W format] " CMD_TARGET_WINDOW_USAGE,
51 	0,
52 	cmd_choose_tree_exec
53 };
54 
55 const struct cmd_entry cmd_choose_session_entry = {
56 	"choose-session", NULL,
57 	"F:t:", 0, 1,
58 	CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
59 	0,
60 	cmd_choose_tree_exec
61 };
62 
63 const struct cmd_entry cmd_choose_window_entry = {
64 	"choose-window", NULL,
65 	"F:t:", 0, 1,
66 	CMD_TARGET_WINDOW_USAGE "[-F format] [template]",
67 	0,
68 	cmd_choose_tree_exec
69 };
70 
71 enum cmd_retval
72 cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq)
73 {
74 	struct args			*args = self->args;
75 	struct winlink			*wl, *wm;
76 	struct session			*s, *s2;
77 	struct client			*c;
78 	struct window_choose_data	*wcd = NULL;
79 	const char			*ses_template, *win_template;
80 	char				*final_win_action, *cur_win_template;
81 	char				*final_win_template_middle;
82 	char				*final_win_template_last;
83 	const char			*ses_action, *win_action;
84 	u_int				 cur_win, idx_ses, win_ses, win_max;
85 	u_int				 wflag, sflag;
86 
87 	ses_template = win_template = NULL;
88 	ses_action = win_action = NULL;
89 
90 	if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) {
91 		cmdq_error(cmdq, "no client available");
92 		return (CMD_RETURN_ERROR);
93 	}
94 
95 	if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL)
96 		return (CMD_RETURN_ERROR);
97 
98 	if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
99 		return (CMD_RETURN_NORMAL);
100 
101 	/* Sort out which command this is. */
102 	wflag = sflag = 0;
103 	if (self->entry == &cmd_choose_session_entry) {
104 		sflag = 1;
105 		if ((ses_template = args_get(args, 'F')) == NULL)
106 			ses_template = CHOOSE_TREE_SESSION_TEMPLATE;
107 
108 		if (args->argc != 0)
109 			ses_action = args->argv[0];
110 		else
111 			ses_action = CMD_CHOOSE_TREE_SESSION_ACTION;
112 	} else if (self->entry == &cmd_choose_window_entry) {
113 		wflag = 1;
114 		if ((win_template = args_get(args, 'F')) == NULL)
115 			win_template = CHOOSE_TREE_WINDOW_TEMPLATE;
116 
117 		if (args->argc != 0)
118 			win_action = args->argv[0];
119 		else
120 			win_action = CMD_CHOOSE_TREE_WINDOW_ACTION;
121 	} else {
122 		wflag = args_has(args, 'w');
123 		sflag = args_has(args, 's');
124 
125 		if ((ses_action = args_get(args, 'b')) == NULL)
126 			ses_action = CMD_CHOOSE_TREE_SESSION_ACTION;
127 
128 		if ((win_action = args_get(args, 'c')) == NULL)
129 			win_action = CMD_CHOOSE_TREE_WINDOW_ACTION;
130 
131 		if ((ses_template = args_get(args, 'S')) == NULL)
132 			ses_template = CHOOSE_TREE_SESSION_TEMPLATE;
133 
134 		if ((win_template = args_get(args, 'W')) == NULL)
135 			win_template = CHOOSE_TREE_WINDOW_TEMPLATE;
136 	}
137 
138 	/*
139 	 * If not asking for windows and sessions, assume no "-ws" given and
140 	 * hence display the entire tree outright.
141 	 */
142 	if (!wflag && !sflag)
143 		wflag = sflag = 1;
144 
145 	/*
146 	 * If we're drawing in tree mode, including sessions, then pad the
147 	 * window template, otherwise just render the windows as a flat list
148 	 * without any padding.
149 	 */
150 	if (wflag && sflag) {
151 		xasprintf(&final_win_template_middle,
152 		    " \001tq\001> %s", win_template);
153 		xasprintf(&final_win_template_last,
154 		    " \001mq\001> %s", win_template);
155 	} else if (wflag) {
156 		final_win_template_middle = xstrdup(win_template);
157 		final_win_template_last = xstrdup(win_template);
158 	} else
159 		final_win_template_middle = final_win_template_last = NULL;
160 
161 	idx_ses = cur_win = -1;
162 	RB_FOREACH(s2, sessions, &sessions) {
163 		idx_ses++;
164 
165 		/*
166 		 * If we're just choosing windows, jump straight there. Note
167 		 * that this implies the current session, so only choose
168 		 * windows when the session matches this one.
169 		 */
170 		if (wflag && !sflag) {
171 			if (s != s2)
172 				continue;
173 			goto windows_only;
174 		}
175 
176 		wcd = window_choose_add_session(wl->window->active,
177 		    c, s2, ses_template, ses_action, idx_ses);
178 
179 		/* If we're just choosing sessions, skip choosing windows. */
180 		if (sflag && !wflag) {
181 			if (s == s2)
182 				cur_win = idx_ses;
183 			continue;
184 		}
185 windows_only:
186 		win_ses = win_max = -1;
187 		RB_FOREACH(wm, winlinks, &s2->windows)
188 			win_max++;
189 		RB_FOREACH(wm, winlinks, &s2->windows) {
190 			win_ses++;
191 			if (sflag && wflag)
192 				idx_ses++;
193 
194 			if (wm == s2->curw && s == s2) {
195 				if (wflag && !sflag) {
196 					/*
197 					 * Then we're only counting windows.
198 					 * So remember which is the current
199 					 * window in the list.
200 					 */
201 					cur_win = win_ses;
202 				} else
203 					cur_win = idx_ses;
204 			}
205 
206 			xasprintf(&final_win_action, "%s %s %s",
207 			    wcd != NULL ? wcd->command : "",
208 			    wcd != NULL ? ";" : "", win_action);
209 
210 			if (win_ses != win_max)
211 				cur_win_template = final_win_template_middle;
212 			else
213 				cur_win_template = final_win_template_last;
214 
215 			window_choose_add_window(wl->window->active,
216 			    c, s2, wm, cur_win_template,
217 			    final_win_action,
218 			    (wflag && !sflag) ? win_ses : idx_ses);
219 
220 			free(final_win_action);
221 		}
222 
223 		/*
224 		 * If we're just drawing windows, don't consider moving on to
225 		 * other sessions as we only list windows in this session.
226 		 */
227 		if (wflag && !sflag)
228 			break;
229 	}
230 	free(final_win_template_middle);
231 	free(final_win_template_last);
232 
233 	window_choose_ready(wl->window->active, cur_win, NULL);
234 
235 	if (args_has(args, 'u')) {
236 		window_choose_expand_all(wl->window->active);
237 		window_choose_set_current(wl->window->active, cur_win);
238 	}
239 
240 	return (CMD_RETURN_NORMAL);
241 }
242