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