xref: /openbsd-src/usr.bin/tmux/cmd-choose-tree.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /* $OpenBSD: cmd-choose-tree.c,v 1.31 2015/12/14 00:31:54 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 	.name = "choose-tree",
48 	.alias = NULL,
49 
50 	.args = { "S:W:swub:c:t:", 0, 1 },
51 	.usage = "[-suw] [-b session-template] [-c window template] "
52 		 "[-S format] [-W format] " CMD_TARGET_WINDOW_USAGE,
53 
54 	.tflag = CMD_WINDOW,
55 
56 	.flags = 0,
57 	.exec = cmd_choose_tree_exec
58 };
59 
60 const struct cmd_entry cmd_choose_session_entry = {
61 	.name = "choose-session",
62 	.alias = NULL,
63 
64 	.args = { "F:t:", 0, 1 },
65 	.usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
66 
67 	.tflag = CMD_WINDOW,
68 
69 	.flags = 0,
70 	.exec = cmd_choose_tree_exec
71 };
72 
73 const struct cmd_entry cmd_choose_window_entry = {
74 	.name = "choose-window",
75 	.alias = NULL,
76 
77 	.args = { "F:t:", 0, 1 },
78 	.usage = CMD_TARGET_WINDOW_USAGE "[-F format] [template]",
79 
80 	.tflag = CMD_WINDOW,
81 
82 	.flags = 0,
83 	.exec = cmd_choose_tree_exec
84 };
85 
86 enum cmd_retval
87 cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq)
88 {
89 	struct args			*args = self->args;
90 	struct client			*c = cmdq->state.c;
91 	struct winlink			*wl = cmdq->state.tflag.wl, *wm;
92 	struct session			*s = cmdq->state.tflag.s, *s2;
93 	struct window_choose_data	*wcd = NULL;
94 	const char			*ses_template, *win_template;
95 	char				*final_win_action, *cur_win_template;
96 	char				*final_win_template_middle;
97 	char				*final_win_template_last;
98 	const char			*ses_action, *win_action;
99 	u_int				 cur_win, idx_ses, win_ses, win_max;
100 	u_int				 wflag, sflag;
101 
102 	ses_template = win_template = NULL;
103 	ses_action = win_action = NULL;
104 
105 	if (c == NULL) {
106 		cmdq_error(cmdq, "no client available");
107 		return (CMD_RETURN_ERROR);
108 	}
109 
110 	if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
111 		return (CMD_RETURN_NORMAL);
112 
113 	/* Sort out which command this is. */
114 	wflag = sflag = 0;
115 	if (self->entry == &cmd_choose_session_entry) {
116 		sflag = 1;
117 		if ((ses_template = args_get(args, 'F')) == NULL)
118 			ses_template = CHOOSE_TREE_SESSION_TEMPLATE;
119 
120 		if (args->argc != 0)
121 			ses_action = args->argv[0];
122 		else
123 			ses_action = CMD_CHOOSE_TREE_SESSION_ACTION;
124 	} else if (self->entry == &cmd_choose_window_entry) {
125 		wflag = 1;
126 		if ((win_template = args_get(args, 'F')) == NULL)
127 			win_template = CHOOSE_TREE_WINDOW_TEMPLATE;
128 
129 		if (args->argc != 0)
130 			win_action = args->argv[0];
131 		else
132 			win_action = CMD_CHOOSE_TREE_WINDOW_ACTION;
133 	} else {
134 		wflag = args_has(args, 'w');
135 		sflag = args_has(args, 's');
136 
137 		if ((ses_action = args_get(args, 'b')) == NULL)
138 			ses_action = CMD_CHOOSE_TREE_SESSION_ACTION;
139 
140 		if ((win_action = args_get(args, 'c')) == NULL)
141 			win_action = CMD_CHOOSE_TREE_WINDOW_ACTION;
142 
143 		if ((ses_template = args_get(args, 'S')) == NULL)
144 			ses_template = CHOOSE_TREE_SESSION_TEMPLATE;
145 
146 		if ((win_template = args_get(args, 'W')) == NULL)
147 			win_template = CHOOSE_TREE_WINDOW_TEMPLATE;
148 	}
149 
150 	/*
151 	 * If not asking for windows and sessions, assume no "-ws" given and
152 	 * hence display the entire tree outright.
153 	 */
154 	if (!wflag && !sflag)
155 		wflag = sflag = 1;
156 
157 	/*
158 	 * If we're drawing in tree mode, including sessions, then pad the
159 	 * window template, otherwise just render the windows as a flat list
160 	 * without any padding.
161 	 */
162 	if (wflag && sflag) {
163 		xasprintf(&final_win_template_middle,
164 		    " \001tq\001> %s", win_template);
165 		xasprintf(&final_win_template_last,
166 		    " \001mq\001> %s", win_template);
167 	} else if (wflag) {
168 		final_win_template_middle = xstrdup(win_template);
169 		final_win_template_last = xstrdup(win_template);
170 	} else
171 		final_win_template_middle = final_win_template_last = NULL;
172 
173 	idx_ses = cur_win = -1;
174 	RB_FOREACH(s2, sessions, &sessions) {
175 		idx_ses++;
176 
177 		/*
178 		 * If we're just choosing windows, jump straight there. Note
179 		 * that this implies the current session, so only choose
180 		 * windows when the session matches this one.
181 		 */
182 		if (wflag && !sflag) {
183 			if (s != s2)
184 				continue;
185 			goto windows_only;
186 		}
187 
188 		wcd = window_choose_add_session(wl->window->active,
189 		    c, s2, ses_template, ses_action, idx_ses);
190 
191 		/* If we're just choosing sessions, skip choosing windows. */
192 		if (sflag && !wflag) {
193 			if (s == s2)
194 				cur_win = idx_ses;
195 			continue;
196 		}
197 windows_only:
198 		win_ses = win_max = -1;
199 		RB_FOREACH(wm, winlinks, &s2->windows)
200 			win_max++;
201 		RB_FOREACH(wm, winlinks, &s2->windows) {
202 			win_ses++;
203 			if (sflag && wflag)
204 				idx_ses++;
205 
206 			if (wm == s2->curw && s == s2) {
207 				if (wflag && !sflag) {
208 					/*
209 					 * Then we're only counting windows.
210 					 * So remember which is the current
211 					 * window in the list.
212 					 */
213 					cur_win = win_ses;
214 				} else
215 					cur_win = idx_ses;
216 			}
217 
218 			xasprintf(&final_win_action, "%s %s %s",
219 			    wcd != NULL ? wcd->command : "",
220 			    wcd != NULL ? ";" : "", win_action);
221 
222 			if (win_ses != win_max)
223 				cur_win_template = final_win_template_middle;
224 			else
225 				cur_win_template = final_win_template_last;
226 
227 			window_choose_add_window(wl->window->active,
228 			    c, s2, wm, cur_win_template,
229 			    final_win_action,
230 			    (wflag && !sflag) ? win_ses : idx_ses);
231 
232 			free(final_win_action);
233 		}
234 
235 		/*
236 		 * If we're just drawing windows, don't consider moving on to
237 		 * other sessions as we only list windows in this session.
238 		 */
239 		if (wflag && !sflag)
240 			break;
241 	}
242 	free(final_win_template_middle);
243 	free(final_win_template_last);
244 
245 	window_choose_ready(wl->window->active, cur_win, NULL);
246 
247 	if (args_has(args, 'u')) {
248 		window_choose_expand_all(wl->window->active);
249 		window_choose_set_current(wl->window->active, cur_win);
250 	}
251 
252 	return (CMD_RETURN_NORMAL);
253 }
254