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