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