Line | Count | Source |
1 | | /* $OpenBSD: window-tree.c,v 1.94 2026/07/14 17:17:18 nicm Exp $ */ |
2 | | |
3 | | /* |
4 | | * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com> |
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 | | #include <string.h> |
24 | | |
25 | | #include "tmux.h" |
26 | | |
27 | | static struct screen *window_tree_init(struct window_mode_entry *, |
28 | | struct cmdq_item *, struct cmd_find_state *, |
29 | | struct args *); |
30 | | static void window_tree_free(struct window_mode_entry *); |
31 | | static void window_tree_resize(struct window_mode_entry *, u_int, |
32 | | u_int); |
33 | | static void window_tree_update(struct window_mode_entry *); |
34 | | static void window_tree_key(struct window_mode_entry *, |
35 | | struct client *, struct session *, |
36 | | struct winlink *, key_code, struct mouse_event *); |
37 | | |
38 | 0 | #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -Zt '%%'" |
39 | | |
40 | | #define WINDOW_TREE_DEFAULT_FORMAT \ |
41 | 0 | "#{?pane_format," \ |
42 | 0 | "#{?pane_marked,#[fg=thememagenta],}#{?pane_floating_flag,#[underscore],}" \ |
43 | 0 | "#{pane_current_command}#[fg=themelightgrey]#{pane_flags}" \ |
44 | 0 | "#{?#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}},: \"#{pane_title}\",}" \ |
45 | 0 | ",window_format," \ |
46 | 0 | "#{?window_marked_flag,#[fg=thememagenta],}" \ |
47 | 0 | "#{window_name}#[fg=themelightgrey]#{window_flags}" \ |
48 | 0 | "#{?#{&&:#{==:#{window_panes},1},#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}}},: \"#{pane_title}\",}" \ |
49 | 0 | "," \ |
50 | 0 | "#[fg=themelightgrey]#{session_windows} windows" \ |
51 | 0 | "#{?session_grouped, " \ |
52 | 0 | "(group #{session_group}: " \ |
53 | 0 | "#{session_group_list})," \ |
54 | 0 | "}" \ |
55 | 0 | "#{?session_attached, (attached),}" \ |
56 | 0 | "}" |
57 | | |
58 | | #define WINDOW_TREE_DEFAULT_KEY_FORMAT \ |
59 | 0 | "#{?#{e|<:#{line},10}," \ |
60 | 0 | "#{line}" \ |
61 | 0 | ",#{e|<:#{line},36}," \ |
62 | 0 | "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \ |
63 | 0 | "}" |
64 | | |
65 | | static const struct menu_item window_tree_menu_items[] = { |
66 | | { "Select", '\r', NULL }, |
67 | | { "Expand", KEYC_RIGHT, NULL }, |
68 | | { "Mark", 'm', NULL }, |
69 | | { "", KEYC_NONE, NULL }, |
70 | | { "Tag", 't', NULL }, |
71 | | { "Tag All", '\024', NULL }, |
72 | | { "Tag None", 'T', NULL }, |
73 | | { "", KEYC_NONE, NULL }, |
74 | | { "Kill", 'x', NULL }, |
75 | | { "Kill Tagged", 'X', NULL }, |
76 | | { "", KEYC_NONE, NULL }, |
77 | | { "Cancel", 'q', NULL }, |
78 | | |
79 | | { NULL, KEYC_NONE, NULL } |
80 | | }; |
81 | | |
82 | | const struct window_mode window_tree_mode = { |
83 | | .name = "tree-mode", |
84 | | .default_format = WINDOW_TREE_DEFAULT_FORMAT, |
85 | | |
86 | | .init = window_tree_init, |
87 | | .free = window_tree_free, |
88 | | .resize = window_tree_resize, |
89 | | .update = window_tree_update, |
90 | | .key = window_tree_key, |
91 | | }; |
92 | | |
93 | | enum window_tree_type { |
94 | | WINDOW_TREE_NONE, |
95 | | WINDOW_TREE_SESSION, |
96 | | WINDOW_TREE_WINDOW, |
97 | | WINDOW_TREE_PANE, |
98 | | }; |
99 | | |
100 | | struct window_tree_itemdata { |
101 | | enum window_tree_type type; |
102 | | int session; |
103 | | int winlink; |
104 | | int pane; |
105 | | }; |
106 | | |
107 | | struct window_tree_modedata { |
108 | | struct window_pane *wp; |
109 | | int dead; |
110 | | int references; |
111 | | |
112 | | struct mode_tree_data *data; |
113 | | char *format; |
114 | | char *key_format; |
115 | | char *command; |
116 | | int squash_groups; |
117 | | int hide_preview_this_pane; |
118 | | int preview_is_info; |
119 | | int prompt_flags; |
120 | | |
121 | | struct window_tree_itemdata **item_list; |
122 | | u_int item_size; |
123 | | |
124 | | const char *entered; |
125 | | |
126 | | struct cmd_find_state fs; |
127 | | enum window_tree_type type; |
128 | | |
129 | | int offset; |
130 | | |
131 | | int left; |
132 | | int right; |
133 | | u_int start; |
134 | | u_int end; |
135 | | u_int each; |
136 | | }; |
137 | | |
138 | | static enum sort_order window_tree_order_seq[] = { |
139 | | SORT_INDEX, |
140 | | SORT_NAME, |
141 | | SORT_ACTIVITY, |
142 | | SORT_Z, |
143 | | SORT_END, |
144 | | }; |
145 | | |
146 | | #define WINDOW_TREE_FLAG(label, cond) \ |
147 | | "#{?" cond ",#[fg=themegreen],#[fg=themelightgrey]}" label "#[default]" |
148 | | |
149 | | static const char *window_tree_pane_info_lines[] = { |
150 | | "#[fg=themelightgrey]Pane #[#{E:tree-mode-border-style},acs]x#[default] " |
151 | | "#{pane_index} #[fg=themelightgrey](#{pane_id})#[default]", |
152 | | "#[fg=themelightgrey]Title #[#{E:tree-mode-border-style},acs]x#[default] " |
153 | | "#{pane_title}", |
154 | | "#[fg=themelightgrey]Command #[#{E:tree-mode-border-style},acs]x#[default] " |
155 | | "#{pane_current_command} #[fg=themelightgrey](PID #{pane_pid})#[default]", |
156 | | "#[fg=themelightgrey]Path #[#{E:tree-mode-border-style},acs]x#[default] " |
157 | | "#{pane_current_path}", |
158 | | "#[fg=themelightgrey]TTY #[#{E:tree-mode-border-style},acs]x#[default] " |
159 | | "#{pane_tty}", |
160 | | "#[fg=themelightgrey]Position #[#{E:tree-mode-border-style},acs]x#[default] " |
161 | | "#{pane_x},#{pane_y} #{pane_width}x#{pane_height}", |
162 | | "#[fg=themelightgrey]Mode #[#{E:tree-mode-border-style},acs]x#[default] " |
163 | | "#{?pane_in_mode,#{pane_mode},none}", |
164 | | "#[fg=themelightgrey]Flags #[#{E:tree-mode-border-style},acs]x#[default] " |
165 | | WINDOW_TREE_FLAG("active", "pane_active") " " |
166 | | WINDOW_TREE_FLAG("zoomed", "window_zoomed_flag") " " |
167 | | WINDOW_TREE_FLAG("marked", "pane_marked") " " |
168 | | WINDOW_TREE_FLAG("sync", "pane_synchronized") " " |
169 | | WINDOW_TREE_FLAG("dead", "pane_dead") " " |
170 | | WINDOW_TREE_FLAG("piped", "pane_pipe"), |
171 | | }; |
172 | | |
173 | | static const char *window_tree_window_info_lines[] = { |
174 | | "#[fg=themelightgrey]Window #[#{E:tree-mode-border-style},acs]x#[default] " |
175 | | "#{window_index}: #{window_name} #[fg=themelightgrey](#{window_id})#[default]", |
176 | | "#[fg=themelightgrey]Size #[#{E:tree-mode-border-style},acs]x#[default] " |
177 | | "#{window_width}x#{window_height}", |
178 | | "#[fg=themelightgrey]Panes #[#{E:tree-mode-border-style},acs]x#[default] " |
179 | | "#{window_panes}", |
180 | | "#[fg=themelightgrey]Activity Time #[#{E:tree-mode-border-style},acs]x#[default] " |
181 | | "#{t:window_activity} #[fg=themelightgrey](#{t/r:window_activity})#[default]", |
182 | | "#[fg=themelightgrey]Sessions #[#{E:tree-mode-border-style},acs]x#[default] " |
183 | | "#{s/,/ /:window_linked_sessions_list}", |
184 | | "#[fg=themelightgrey]Flags #[#{E:tree-mode-border-style},acs]x#[default] " |
185 | | WINDOW_TREE_FLAG("active", "window_active") " " |
186 | | WINDOW_TREE_FLAG("last", "window_last_flag") " " |
187 | | WINDOW_TREE_FLAG("bell", "window_bell_flag") " " |
188 | | WINDOW_TREE_FLAG("activity", "window_activity_flag") " " |
189 | | WINDOW_TREE_FLAG("silence", "window_silence_flag") " " |
190 | | WINDOW_TREE_FLAG("zoomed", "window_zoomed_flag") " " |
191 | | WINDOW_TREE_FLAG("marked", "window_marked_flag"), |
192 | | }; |
193 | | |
194 | | static const char *window_tree_session_info_lines[] = { |
195 | | "#[fg=themelightgrey]Session #[#{E:tree-mode-border-style},acs]x#[default] " |
196 | | "#{session_name} #[fg=themelightgrey](#{session_id})#[default]", |
197 | | "#[fg=themelightgrey]Created Time #[#{E:tree-mode-border-style},acs]x#[default] " |
198 | | "#{t:session_created} #[fg=themelightgrey](#{t/r:session_created})#[default]", |
199 | | "#[fg=themelightgrey]Activity Time #[#{E:tree-mode-border-style},acs]x#[default] " |
200 | | "#{t:session_activity} #[fg=themelightgrey](#{t/r:session_activity})#[default]", |
201 | | "#[fg=themelightgrey]Attached Time #[#{E:tree-mode-border-style},acs]x#[default] " |
202 | | "#{?#{t:session_last_attached},#{t:session_last_attached} #[fg=themelightgrey](#{t/r:session_last_attached})#[default],never}", |
203 | | "#[fg=themelightgrey]Clients #[#{E:tree-mode-border-style},acs]x#[default] " |
204 | | "#{s/,/ /:session_attached_list}", |
205 | | "#[fg=themelightgrey]Windows #[#{E:tree-mode-border-style},acs]x#[default] " |
206 | | "#{session_windows}", |
207 | | "#[fg=themelightgrey]Path #[#{E:tree-mode-border-style},acs]x#[default] " |
208 | | "#{session_path}", |
209 | | "#[fg=themelightgrey]Group #[#{E:tree-mode-border-style},acs]x#[default] " |
210 | | "#{?session_grouped,#{session_group} (#{session_group_size}),none}", |
211 | | "#[fg=themelightgrey]Flags #[#{E:tree-mode-border-style},acs]x#[default] " |
212 | | WINDOW_TREE_FLAG("attached", "session_attached") " " |
213 | | WINDOW_TREE_FLAG("grouped", "session_grouped") " " |
214 | | WINDOW_TREE_FLAG("marked", "session_marked") " " |
215 | | WINDOW_TREE_FLAG("bell", "session_bell_flag") " " |
216 | | WINDOW_TREE_FLAG("activity", "session_activity_flag") " " |
217 | | WINDOW_TREE_FLAG("silence", "session_silence_flag"), |
218 | | }; |
219 | | |
220 | | static void |
221 | | window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp, |
222 | | struct winlink **wlp, struct window_pane **wp) |
223 | 0 | { |
224 | 0 | *wp = NULL; |
225 | 0 | *wlp = NULL; |
226 | 0 | *sp = session_find_by_id(item->session); |
227 | 0 | if (*sp == NULL) |
228 | 0 | return; |
229 | 0 | if (item->type == WINDOW_TREE_SESSION) { |
230 | 0 | *wlp = (*sp)->curw; |
231 | 0 | *wp = (*wlp)->window->active; |
232 | 0 | return; |
233 | 0 | } |
234 | | |
235 | 0 | *wlp = winlink_find_by_index(&(*sp)->windows, item->winlink); |
236 | 0 | if (*wlp == NULL) { |
237 | 0 | *sp = NULL; |
238 | 0 | return; |
239 | 0 | } |
240 | 0 | if (item->type == WINDOW_TREE_WINDOW) { |
241 | 0 | *wp = (*wlp)->window->active; |
242 | 0 | return; |
243 | 0 | } |
244 | | |
245 | 0 | *wp = window_pane_find_by_id(item->pane); |
246 | 0 | if (!window_has_pane((*wlp)->window, *wp)) |
247 | 0 | *wp = NULL; |
248 | 0 | if (*wp == NULL) { |
249 | 0 | *sp = NULL; |
250 | 0 | *wlp = NULL; |
251 | 0 | return; |
252 | 0 | } |
253 | 0 | } |
254 | | |
255 | | static struct window_tree_itemdata * |
256 | | window_tree_add_item(struct window_tree_modedata *data) |
257 | 0 | { |
258 | 0 | struct window_tree_itemdata *item; |
259 | |
|
260 | 0 | data->item_list = xreallocarray(data->item_list, data->item_size + 1, |
261 | 0 | sizeof *data->item_list); |
262 | 0 | item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); |
263 | 0 | return (item); |
264 | 0 | } |
265 | | |
266 | | static void |
267 | | window_tree_free_item(struct window_tree_itemdata *item) |
268 | 0 | { |
269 | 0 | free(item); |
270 | 0 | } |
271 | | |
272 | | static void |
273 | | window_tree_build_pane(struct session *s, struct winlink *wl, |
274 | | struct window_pane *wp, void *modedata, struct mode_tree_item *parent) |
275 | 0 | { |
276 | 0 | struct window_tree_modedata *data = modedata; |
277 | 0 | struct window_tree_itemdata *item; |
278 | 0 | struct mode_tree_item *mti; |
279 | 0 | char *name, *text; |
280 | 0 | u_int idx; |
281 | 0 | struct format_tree *ft; |
282 | |
|
283 | 0 | window_pane_index(wp, &idx); |
284 | |
|
285 | 0 | item = window_tree_add_item(data); |
286 | 0 | item->type = WINDOW_TREE_PANE; |
287 | 0 | item->session = s->id; |
288 | 0 | item->winlink = wl->idx; |
289 | 0 | item->pane = wp->id; |
290 | |
|
291 | 0 | ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 0); |
292 | 0 | format_defaults(ft, NULL, s, wl, wp); |
293 | 0 | text = format_expand(ft, data->format); |
294 | 0 | xasprintf(&name, "%u", idx); |
295 | 0 | format_free(ft); |
296 | |
|
297 | 0 | mti = mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, |
298 | 0 | -1); |
299 | 0 | free(text); |
300 | 0 | free(name); |
301 | 0 | mode_tree_align(mti, 1); |
302 | 0 | } |
303 | | |
304 | | static int |
305 | | window_tree_filter_pane(struct session *s, struct winlink *wl, |
306 | | struct window_pane *wp, const char *filter) |
307 | 0 | { |
308 | 0 | char *cp; |
309 | 0 | int result; |
310 | |
|
311 | 0 | if (filter == NULL) |
312 | 0 | return (1); |
313 | | |
314 | 0 | cp = format_single(NULL, filter, NULL, s, wl, wp); |
315 | 0 | result = format_true(cp); |
316 | 0 | free(cp); |
317 | |
|
318 | 0 | return (result); |
319 | 0 | } |
320 | | |
321 | | static int |
322 | | window_tree_build_window(struct session *s, struct winlink *wl, |
323 | | void *modedata, struct sort_criteria *sort_crit, |
324 | | struct mode_tree_item *parent, const char *filter) |
325 | 0 | { |
326 | 0 | struct window_tree_modedata *data = modedata; |
327 | 0 | struct window_tree_itemdata *item; |
328 | 0 | struct mode_tree_item *mti; |
329 | 0 | char *name, *text; |
330 | 0 | struct window_pane **l; |
331 | 0 | u_int n, i, found; |
332 | 0 | int expanded; |
333 | 0 | struct format_tree *ft; |
334 | 0 | uint64_t tag = FORMAT_NONE; |
335 | |
|
336 | 0 | item = window_tree_add_item(data); |
337 | 0 | item->type = WINDOW_TREE_WINDOW; |
338 | 0 | item->session = s->id; |
339 | 0 | item->winlink = wl->idx; |
340 | 0 | item->pane = -1; |
341 | |
|
342 | 0 | if (wl->window != NULL && wl->window->active != NULL) |
343 | 0 | tag = FORMAT_PANE|wl->window->active->id; |
344 | 0 | ft = format_create(NULL, NULL, tag, 0); |
345 | 0 | format_defaults(ft, NULL, s, wl, NULL); |
346 | 0 | text = format_expand(ft, data->format); |
347 | 0 | xasprintf(&name, "%u", wl->idx); |
348 | 0 | format_free(ft); |
349 | |
|
350 | 0 | if (data->type == WINDOW_TREE_SESSION || |
351 | 0 | data->type == WINDOW_TREE_WINDOW) |
352 | 0 | expanded = 0; |
353 | 0 | else |
354 | 0 | expanded = 1; |
355 | 0 | mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text, |
356 | 0 | expanded); |
357 | 0 | free(text); |
358 | 0 | free(name); |
359 | 0 | mode_tree_align(mti, 1); |
360 | |
|
361 | 0 | l = sort_get_panes_window(wl->window, &n, sort_crit); |
362 | 0 | found = 0; |
363 | 0 | for (i = 0; i < n; i++) { |
364 | 0 | if (!window_tree_filter_pane(s, wl, l[i], filter)) |
365 | 0 | continue; |
366 | 0 | found++; |
367 | 0 | if (data->hide_preview_this_pane && l[i] == data->wp) |
368 | 0 | continue; |
369 | 0 | window_tree_build_pane(s, wl, l[i], modedata, mti); |
370 | 0 | } |
371 | 0 | if (found == 0) { |
372 | 0 | window_tree_free_item(item); |
373 | 0 | data->item_size--; |
374 | 0 | mode_tree_remove(data->data, mti); |
375 | 0 | return (0); |
376 | 0 | } |
377 | 0 | return (1); |
378 | 0 | } |
379 | | |
380 | | static void |
381 | | window_tree_build_session(struct session *s, void *modedata, |
382 | | struct sort_criteria *sort_crit, const char *filter) |
383 | 0 | { |
384 | 0 | struct window_tree_modedata *data = modedata; |
385 | 0 | struct window_tree_itemdata *item; |
386 | 0 | struct mode_tree_item *mti; |
387 | 0 | char *text; |
388 | 0 | struct winlink *wl = s->curw, **l; |
389 | 0 | u_int n, i, empty; |
390 | 0 | int expanded; |
391 | 0 | struct format_tree *ft; |
392 | 0 | uint64_t tag = FORMAT_NONE; |
393 | |
|
394 | 0 | item = window_tree_add_item(data); |
395 | 0 | item->type = WINDOW_TREE_SESSION; |
396 | 0 | item->session = s->id; |
397 | 0 | item->winlink = -1; |
398 | 0 | item->pane = -1; |
399 | |
|
400 | 0 | if (wl != NULL && wl->window != NULL && wl->window->active != NULL) |
401 | 0 | tag = FORMAT_PANE|wl->window->active->id; |
402 | 0 | ft = format_create(NULL, NULL, tag, 0); |
403 | 0 | format_defaults(ft, NULL, s, NULL, NULL); |
404 | 0 | text = format_expand(ft, data->format); |
405 | 0 | format_free(ft); |
406 | |
|
407 | 0 | if (data->type == WINDOW_TREE_SESSION) |
408 | 0 | expanded = 0; |
409 | 0 | else |
410 | 0 | expanded = 1; |
411 | 0 | mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text, |
412 | 0 | expanded); |
413 | 0 | free(text); |
414 | |
|
415 | 0 | l = sort_get_winlinks_session(s, &n, sort_crit); |
416 | 0 | empty = 0; |
417 | 0 | for (i = 0; i < n; i++) { |
418 | 0 | if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti, |
419 | 0 | filter)) |
420 | 0 | empty++; |
421 | 0 | } |
422 | 0 | if (empty == n) { |
423 | 0 | window_tree_free_item(item); |
424 | 0 | data->item_size--; |
425 | 0 | mode_tree_remove(data->data, mti); |
426 | 0 | } |
427 | 0 | } |
428 | | |
429 | | static void |
430 | | window_tree_build(void *modedata, struct sort_criteria *sort_crit, |
431 | | uint64_t *tag, const char *filter) |
432 | 0 | { |
433 | 0 | struct window_tree_modedata *data = modedata; |
434 | 0 | int squash_groups = data->squash_groups; |
435 | 0 | struct session *s, **l; |
436 | 0 | struct session_group *sg, *current; |
437 | 0 | u_int n, i; |
438 | |
|
439 | 0 | current = session_group_contains(data->fs.s); |
440 | |
|
441 | 0 | for (i = 0; i < data->item_size; i++) |
442 | 0 | window_tree_free_item(data->item_list[i]); |
443 | 0 | free(data->item_list); |
444 | 0 | data->item_list = NULL; |
445 | 0 | data->item_size = 0; |
446 | |
|
447 | 0 | l = sort_get_sessions(&n, sort_crit); |
448 | 0 | if (n == 0) |
449 | 0 | return; |
450 | 0 | for (i = 0; i < n; i++) { |
451 | 0 | s = l[i]; |
452 | 0 | if (squash_groups && (sg = session_group_contains(s)) != NULL) { |
453 | 0 | if ((sg == current && s != data->fs.s) || |
454 | 0 | (sg != current && s != TAILQ_FIRST(&sg->sessions))) |
455 | 0 | continue; |
456 | 0 | } |
457 | 0 | window_tree_build_session(s, modedata, sort_crit, filter); |
458 | 0 | } |
459 | |
|
460 | 0 | switch (data->type) { |
461 | 0 | case WINDOW_TREE_NONE: |
462 | 0 | break; |
463 | 0 | case WINDOW_TREE_SESSION: |
464 | 0 | *tag = (uint64_t)data->fs.s; |
465 | 0 | break; |
466 | 0 | case WINDOW_TREE_WINDOW: |
467 | 0 | *tag = (uint64_t)data->fs.wl; |
468 | 0 | break; |
469 | 0 | case WINDOW_TREE_PANE: |
470 | 0 | if (window_count_panes(data->fs.wl->window, 1) == 1) |
471 | 0 | *tag = (uint64_t)data->fs.wl; |
472 | 0 | else |
473 | 0 | *tag = (uint64_t)data->fs.wp; |
474 | 0 | break; |
475 | 0 | } |
476 | 0 | } |
477 | | |
478 | | static void |
479 | | window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py, |
480 | | u_int sx, u_int sy, const struct grid_cell *border_gc, |
481 | | const struct grid_cell *label_gc, const char *label) |
482 | 0 | { |
483 | 0 | u_int width, ox, oy; |
484 | 0 | char *new_label = NULL; |
485 | |
|
486 | 0 | if (sx < 5 || sy < 3) |
487 | 0 | return; |
488 | 0 | width = format_width(label); |
489 | 0 | if (width > sx - 4) { |
490 | 0 | label = new_label = format_trim_left(label, sx - 4); |
491 | 0 | width = format_width(new_label); |
492 | 0 | } |
493 | 0 | if (width == 0) |
494 | 0 | return; |
495 | 0 | ox = (sx - width + 1) / 2; |
496 | 0 | oy = (sy + 1) / 2; |
497 | |
|
498 | 0 | screen_write_cursormove(ctx, px + ox - 2, py + oy - 1, 0); |
499 | 0 | screen_write_box(ctx, width + 4, 3, BOX_LINES_DEFAULT, |
500 | 0 | border_gc, NULL); |
501 | 0 | screen_write_cursormove(ctx, px + ox - 1, py + oy, 0); |
502 | 0 | screen_write_clearcharacter(ctx, width + 2, border_gc->bg); |
503 | 0 | screen_write_cursormove(ctx, px + ox, py + oy, 0); |
504 | 0 | format_draw(ctx, label_gc, width, label, NULL, 0); |
505 | 0 | free(new_label); |
506 | 0 | } |
507 | | |
508 | | static void |
509 | | window_tree_border_cell(struct grid_cell *gc, struct options *oo, |
510 | | struct format_tree *ft) |
511 | 0 | { |
512 | 0 | memcpy(gc, &grid_default_cell, sizeof *gc); |
513 | 0 | style_apply(gc, oo, "tree-mode-border-style", ft); |
514 | 0 | } |
515 | | |
516 | | static void |
517 | | window_tree_draw_session(struct window_tree_modedata *data, struct session *s, |
518 | | struct screen_write_ctx *ctx, u_int sx, u_int sy) |
519 | 0 | { |
520 | 0 | struct winlink *wl; |
521 | 0 | struct window *w; |
522 | 0 | u_int cx = ctx->s->cx, cy = ctx->s->cy; |
523 | 0 | u_int loop, total, visible, each, width, offset; |
524 | 0 | u_int current, start, end, remaining, i; |
525 | 0 | struct grid_cell gc, label_gc; |
526 | 0 | int left, right; |
527 | 0 | char *label; |
528 | 0 | const char *format; |
529 | 0 | struct format_tree *ft; |
530 | 0 | struct options *oo; |
531 | |
|
532 | 0 | total = winlink_count(&s->windows); |
533 | |
|
534 | 0 | if (sx / total < 24) { |
535 | 0 | visible = sx / 24; |
536 | 0 | if (visible == 0) |
537 | 0 | visible = 1; |
538 | 0 | } else |
539 | 0 | visible = total; |
540 | |
|
541 | 0 | current = 0; |
542 | 0 | RB_FOREACH(wl, winlinks, &s->windows) { |
543 | 0 | if (wl == s->curw) |
544 | 0 | break; |
545 | 0 | current++; |
546 | 0 | } |
547 | |
|
548 | 0 | if (current < visible) { |
549 | 0 | start = 0; |
550 | 0 | end = visible; |
551 | 0 | } else if (current >= total - visible) { |
552 | 0 | start = total - visible; |
553 | 0 | end = total; |
554 | 0 | } else { |
555 | 0 | start = current - (visible / 2); |
556 | 0 | end = start + visible; |
557 | 0 | } |
558 | |
|
559 | 0 | if (data->offset < -(int)start) |
560 | 0 | data->offset = -(int)start; |
561 | 0 | if (data->offset > (int)(total - end)) |
562 | 0 | data->offset = (int)(total - end); |
563 | 0 | start += data->offset; |
564 | 0 | end += data->offset; |
565 | |
|
566 | 0 | left = (start != 0); |
567 | 0 | right = (end != total); |
568 | 0 | if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) |
569 | 0 | left = right = 0; |
570 | 0 | if (left && right) { |
571 | 0 | each = (sx - 6) / visible; |
572 | 0 | remaining = (sx - 6) - (visible * each); |
573 | 0 | } else if (left || right) { |
574 | 0 | each = (sx - 3) / visible; |
575 | 0 | remaining = (sx - 3) - (visible * each); |
576 | 0 | } else { |
577 | 0 | each = sx / visible; |
578 | 0 | remaining = sx - (visible * each); |
579 | 0 | } |
580 | 0 | if (each == 0) |
581 | 0 | return; |
582 | | |
583 | 0 | window_tree_border_cell(&gc, data->wp->window->options, NULL); |
584 | 0 | if (left) { |
585 | 0 | data->left = cx + 2; |
586 | 0 | screen_write_cursormove(ctx, cx + 2, cy, 0); |
587 | 0 | screen_write_vline(ctx, sy, 0, 0, &gc); |
588 | 0 | screen_write_cursormove(ctx, cx, cy + sy / 2, 0); |
589 | 0 | screen_write_puts(ctx, &gc, "<"); |
590 | 0 | } else |
591 | 0 | data->left = -1; |
592 | 0 | if (right) { |
593 | 0 | data->right = cx + sx - 3; |
594 | 0 | screen_write_cursormove(ctx, cx + sx - 3, cy, 0); |
595 | 0 | screen_write_vline(ctx, sy, 0, 0, &gc); |
596 | 0 | screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0); |
597 | 0 | screen_write_puts(ctx, &gc, ">"); |
598 | 0 | } else |
599 | 0 | data->right = -1; |
600 | |
|
601 | 0 | data->start = start; |
602 | 0 | data->end = end; |
603 | 0 | data->each = each; |
604 | |
|
605 | 0 | i = loop = 0; |
606 | 0 | RB_FOREACH(wl, winlinks, &s->windows) { |
607 | 0 | if (loop == end) |
608 | 0 | break; |
609 | 0 | if (loop < start) { |
610 | 0 | loop++; |
611 | 0 | continue; |
612 | 0 | } |
613 | 0 | w = wl->window; |
614 | 0 | oo = w->options; |
615 | |
|
616 | 0 | ft = format_create(NULL, NULL, FORMAT_WINDOW|w->id, 0); |
617 | 0 | format_defaults(ft, NULL, s, wl, NULL); |
618 | |
|
619 | 0 | window_tree_border_cell(&gc, oo, ft); |
620 | 0 | memcpy(&label_gc, &grid_default_cell, sizeof label_gc); |
621 | 0 | style_apply(&label_gc, oo, "tree-mode-preview-style", ft); |
622 | 0 | label_gc.bg = gc.bg; |
623 | |
|
624 | 0 | if (left) |
625 | 0 | offset = 3 + (i * each); |
626 | 0 | else |
627 | 0 | offset = (i * each); |
628 | 0 | if (loop == end - 1) |
629 | 0 | width = each + remaining; |
630 | 0 | else |
631 | 0 | width = each - 1; |
632 | |
|
633 | 0 | screen_write_cursormove(ctx, cx + offset, cy, 0); |
634 | 0 | screen_write_preview(ctx, &w->active->base, width, sy); |
635 | |
|
636 | 0 | format = options_get_string(oo, "tree-mode-preview-format"); |
637 | 0 | if (*format != '\0') { |
638 | 0 | label = format_expand(ft, format); |
639 | 0 | if (*label != '\0') { |
640 | 0 | window_tree_draw_label(ctx, cx + offset, cy, |
641 | 0 | width, sy, &gc, &label_gc, label); |
642 | 0 | } |
643 | 0 | free(label); |
644 | 0 | } |
645 | |
|
646 | 0 | if (loop != end - 1) { |
647 | 0 | screen_write_cursormove(ctx, cx + offset + width, cy, |
648 | 0 | 0); |
649 | 0 | screen_write_vline(ctx, sy, 0, 0, &gc); |
650 | 0 | } |
651 | 0 | loop++; |
652 | |
|
653 | 0 | i++; |
654 | 0 | } |
655 | 0 | } |
656 | | |
657 | | static void |
658 | | window_tree_draw_window(struct window_tree_modedata *data, struct session *s, |
659 | | struct winlink *wl, struct screen_write_ctx *ctx, u_int sx, u_int sy) |
660 | 0 | { |
661 | 0 | struct window *w = wl->window; |
662 | 0 | struct window_pane *wp; |
663 | 0 | u_int cx = ctx->s->cx, cy = ctx->s->cy; |
664 | 0 | u_int loop, total, visible, each, width, offset; |
665 | 0 | u_int current, start, end, remaining, i; |
666 | 0 | struct grid_cell gc, label_gc; |
667 | 0 | int left, right; |
668 | 0 | char *label; |
669 | 0 | const char *format; |
670 | 0 | struct format_tree *ft; |
671 | 0 | struct options *oo; |
672 | |
|
673 | 0 | total = window_count_panes(w, 1); |
674 | 0 | if (data->hide_preview_this_pane && data->wp->window == w) |
675 | 0 | total--; |
676 | 0 | if (total == 0) |
677 | 0 | return; |
678 | | |
679 | 0 | if (sx / total < 24) { |
680 | 0 | visible = sx / 24; |
681 | 0 | if (visible == 0) |
682 | 0 | visible = 1; |
683 | 0 | } else |
684 | 0 | visible = total; |
685 | |
|
686 | 0 | current = 0; |
687 | 0 | TAILQ_FOREACH(wp, &w->panes, entry) { |
688 | 0 | if (data->hide_preview_this_pane && wp == data->wp) |
689 | 0 | continue; |
690 | 0 | if (wp == w->active) |
691 | 0 | break; |
692 | 0 | current++; |
693 | 0 | } |
694 | |
|
695 | 0 | if (current < visible) { |
696 | 0 | start = 0; |
697 | 0 | end = visible; |
698 | 0 | } else if (current >= total - visible) { |
699 | 0 | start = total - visible; |
700 | 0 | end = total; |
701 | 0 | } else { |
702 | 0 | start = current - (visible / 2); |
703 | 0 | end = start + visible; |
704 | 0 | } |
705 | |
|
706 | 0 | if (data->offset < -(int)start) |
707 | 0 | data->offset = -(int)start; |
708 | 0 | if (data->offset > (int)(total - end)) |
709 | 0 | data->offset = (int)(total - end); |
710 | 0 | start += data->offset; |
711 | 0 | end += data->offset; |
712 | |
|
713 | 0 | left = (start != 0); |
714 | 0 | right = (end != total); |
715 | 0 | if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) |
716 | 0 | left = right = 0; |
717 | 0 | if (left && right) { |
718 | 0 | each = (sx - 6) / visible; |
719 | 0 | remaining = (sx - 6) - (visible * each); |
720 | 0 | } else if (left || right) { |
721 | 0 | each = (sx - 3) / visible; |
722 | 0 | remaining = (sx - 3) - (visible * each); |
723 | 0 | } else { |
724 | 0 | each = sx / visible; |
725 | 0 | remaining = sx - (visible * each); |
726 | 0 | } |
727 | 0 | if (each == 0) |
728 | 0 | return; |
729 | | |
730 | 0 | window_tree_border_cell(&gc, data->wp->window->options, NULL); |
731 | 0 | if (left) { |
732 | 0 | data->left = cx + 2; |
733 | 0 | screen_write_cursormove(ctx, cx + 2, cy, 0); |
734 | 0 | screen_write_vline(ctx, sy, 0, 0, &gc); |
735 | 0 | screen_write_cursormove(ctx, cx, cy + sy / 2, 0); |
736 | 0 | screen_write_puts(ctx, &gc, "<"); |
737 | 0 | } else |
738 | 0 | data->left = -1; |
739 | 0 | if (right) { |
740 | 0 | data->right = cx + sx - 3; |
741 | 0 | screen_write_cursormove(ctx, cx + sx - 3, cy, 0); |
742 | 0 | screen_write_vline(ctx, sy, 0, 0, &gc); |
743 | 0 | screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0); |
744 | 0 | screen_write_puts(ctx, &gc, ">"); |
745 | 0 | } else |
746 | 0 | data->right = -1; |
747 | |
|
748 | 0 | data->start = start; |
749 | 0 | data->end = end; |
750 | 0 | data->each = each; |
751 | |
|
752 | 0 | i = loop = 0; |
753 | 0 | TAILQ_FOREACH(wp, &w->panes, entry) { |
754 | 0 | if (data->hide_preview_this_pane && wp == data->wp) |
755 | 0 | continue; |
756 | 0 | if (loop == end) |
757 | 0 | break; |
758 | 0 | if (loop < start) { |
759 | 0 | loop++; |
760 | 0 | continue; |
761 | 0 | } |
762 | 0 | oo = wp->options; |
763 | |
|
764 | 0 | ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 0); |
765 | 0 | format_defaults(ft, NULL, s, wl, wp); |
766 | |
|
767 | 0 | window_tree_border_cell(&gc, oo, ft); |
768 | 0 | memcpy(&label_gc, &grid_default_cell, sizeof label_gc); |
769 | 0 | style_apply(&label_gc, oo, "tree-mode-preview-style", ft); |
770 | 0 | label_gc.bg = gc.bg; |
771 | |
|
772 | 0 | if (left) |
773 | 0 | offset = 3 + (i * each); |
774 | 0 | else |
775 | 0 | offset = (i * each); |
776 | 0 | if (loop == end - 1) |
777 | 0 | width = each + remaining; |
778 | 0 | else |
779 | 0 | width = each - 1; |
780 | |
|
781 | 0 | screen_write_cursormove(ctx, cx + offset, cy, 0); |
782 | 0 | screen_write_preview(ctx, &wp->base, width, sy); |
783 | |
|
784 | 0 | format = options_get_string(oo, "tree-mode-preview-format"); |
785 | 0 | if (*format != '\0') { |
786 | 0 | label = format_expand(ft, format); |
787 | 0 | if (*label != '\0') { |
788 | 0 | window_tree_draw_label(ctx, cx + offset, cy, |
789 | 0 | width, sy, &gc, &label_gc, label); |
790 | 0 | } |
791 | 0 | free(label); |
792 | 0 | } |
793 | |
|
794 | 0 | if (loop != end - 1) { |
795 | 0 | screen_write_cursormove(ctx, cx + offset + width, cy, |
796 | 0 | 0); |
797 | 0 | screen_write_vline(ctx, sy, 0, 0, &gc); |
798 | 0 | } |
799 | 0 | loop++; |
800 | |
|
801 | 0 | i++; |
802 | 0 | } |
803 | 0 | } |
804 | | |
805 | | static void |
806 | | window_tree_draw_info(struct window_tree_modedata *data, void *itemdata, |
807 | | struct screen_write_ctx *ctx, u_int sx, u_int sy) |
808 | 0 | { |
809 | 0 | struct window_tree_itemdata *item = itemdata; |
810 | 0 | struct screen *s = ctx->s; |
811 | 0 | struct session *sp; |
812 | 0 | struct winlink *wl; |
813 | 0 | struct window_pane *wp; |
814 | 0 | struct grid_cell gc; |
815 | 0 | u_int cx = s->cx, cy = s->cy, i, j, k; |
816 | 0 | struct format_tree *ft; |
817 | 0 | char *expanded; |
818 | 0 | const char *const *lines[3]; |
819 | 0 | u_int count[3], n = 0; |
820 | |
|
821 | 0 | window_tree_pull_item(item, &sp, &wl, &wp); |
822 | 0 | if (sp == NULL || wp == NULL) |
823 | 0 | return; |
824 | | |
825 | 0 | if (item->type == WINDOW_TREE_PANE) { |
826 | 0 | lines[n] = window_tree_pane_info_lines; |
827 | 0 | count[n++] = nitems(window_tree_pane_info_lines); |
828 | 0 | } |
829 | 0 | if (item->type == WINDOW_TREE_PANE || |
830 | 0 | item->type == WINDOW_TREE_WINDOW) { |
831 | 0 | lines[n] = window_tree_window_info_lines; |
832 | 0 | count[n++] = nitems(window_tree_window_info_lines); |
833 | 0 | } |
834 | 0 | lines[n] = window_tree_session_info_lines; |
835 | 0 | count[n++] = nitems(window_tree_session_info_lines); |
836 | |
|
837 | 0 | ft = format_create(NULL, NULL, FORMAT_NONE, 0); |
838 | 0 | format_defaults(ft, NULL, sp, wl, wp); |
839 | |
|
840 | 0 | i = 0; |
841 | 0 | for (j = 0; j < n; j++) { |
842 | 0 | if (j != 0) { |
843 | 0 | if (i == sy) |
844 | 0 | break; |
845 | 0 | window_tree_border_cell(&gc, data->wp->window->options, |
846 | 0 | NULL); |
847 | 0 | screen_write_cursormove(ctx, cx, cy + i, 0); |
848 | 0 | screen_write_hline(ctx, sx, 0, 0, BOX_LINES_DEFAULT, |
849 | 0 | &gc); |
850 | 0 | if (sx > 14) { |
851 | 0 | gc.attr |= GRID_ATTR_CHARSET; |
852 | 0 | screen_write_cursormove(ctx, cx + 14, cy + i, |
853 | 0 | 0); |
854 | 0 | screen_write_putc(ctx, &gc, 'n'); |
855 | 0 | } |
856 | 0 | i++; |
857 | 0 | } |
858 | 0 | for (k = 0; k < count[j]; k++) { |
859 | 0 | if (i == sy) |
860 | 0 | break; |
861 | 0 | expanded = format_expand(ft, lines[j][k]); |
862 | 0 | screen_write_cursormove(ctx, cx, cy + i, 0); |
863 | 0 | format_draw(ctx, &grid_default_cell, sx, expanded, NULL, |
864 | 0 | 0); |
865 | 0 | free(expanded); |
866 | 0 | i++; |
867 | 0 | } |
868 | 0 | if (i == sy) |
869 | 0 | break; |
870 | 0 | } |
871 | 0 | if (sx > 14 && i < sy) { |
872 | 0 | window_tree_border_cell(&gc, data->wp->window->options, NULL); |
873 | 0 | screen_write_cursormove(ctx, cx + 14, cy + i, 0); |
874 | 0 | screen_write_vline(ctx, sy - i, 0, 0, &gc); |
875 | 0 | } |
876 | |
|
877 | 0 | format_free(ft); |
878 | 0 | } |
879 | | |
880 | | static void |
881 | | window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, |
882 | | u_int sx, u_int sy) |
883 | 0 | { |
884 | 0 | struct window_tree_modedata *data = modedata; |
885 | 0 | struct window_tree_itemdata *item = itemdata; |
886 | 0 | struct session *sp; |
887 | 0 | struct winlink *wl; |
888 | 0 | struct window_pane *wp; |
889 | |
|
890 | 0 | window_tree_pull_item(item, &sp, &wl, &wp); |
891 | 0 | if (wp == NULL) |
892 | 0 | return; |
893 | | |
894 | 0 | if (data->preview_is_info) { |
895 | 0 | window_tree_draw_info(data, item, ctx, sx, sy); |
896 | 0 | return; |
897 | 0 | } |
898 | | |
899 | 0 | switch (item->type) { |
900 | 0 | case WINDOW_TREE_NONE: |
901 | 0 | break; |
902 | 0 | case WINDOW_TREE_SESSION: |
903 | 0 | window_tree_draw_session(modedata, sp, ctx, sx, sy); |
904 | 0 | break; |
905 | 0 | case WINDOW_TREE_WINDOW: |
906 | 0 | window_tree_draw_window(modedata, sp, wl, ctx, sx, sy); |
907 | 0 | break; |
908 | 0 | case WINDOW_TREE_PANE: |
909 | 0 | if (!data->hide_preview_this_pane || wp != data->wp) |
910 | 0 | screen_write_preview(ctx, &wp->base, sx, sy); |
911 | 0 | break; |
912 | 0 | } |
913 | 0 | } |
914 | | |
915 | | static int |
916 | | window_tree_search(__unused void *modedata, void *itemdata, const char *ss, |
917 | | int icase) |
918 | 0 | { |
919 | 0 | struct window_tree_itemdata *item = itemdata; |
920 | 0 | struct session *s; |
921 | 0 | struct winlink *wl; |
922 | 0 | struct window_pane *wp; |
923 | 0 | char *cmd; |
924 | 0 | int retval; |
925 | |
|
926 | 0 | window_tree_pull_item(item, &s, &wl, &wp); |
927 | |
|
928 | 0 | switch (item->type) { |
929 | 0 | case WINDOW_TREE_NONE: |
930 | 0 | return (0); |
931 | 0 | case WINDOW_TREE_SESSION: |
932 | 0 | if (s == NULL) |
933 | 0 | return (0); |
934 | 0 | if (icase) |
935 | 0 | return (strcasestr(s->name, ss) != NULL); |
936 | 0 | return (strstr(s->name, ss) != NULL); |
937 | 0 | case WINDOW_TREE_WINDOW: |
938 | 0 | if (s == NULL || wl == NULL) |
939 | 0 | return (0); |
940 | 0 | if (icase) |
941 | 0 | return (strcasestr(wl->window->name, ss) != NULL); |
942 | 0 | return (strstr(wl->window->name, ss) != NULL); |
943 | 0 | case WINDOW_TREE_PANE: |
944 | 0 | if (s == NULL || wl == NULL || wp == NULL) |
945 | 0 | break; |
946 | 0 | cmd = osdep_get_name(wp->fd, wp->tty); |
947 | 0 | if (cmd == NULL || *cmd == '\0') { |
948 | 0 | free(cmd); |
949 | 0 | return (0); |
950 | 0 | } |
951 | 0 | if (icase) |
952 | 0 | retval = (strcasestr(cmd, ss) != NULL); |
953 | 0 | else |
954 | 0 | retval = (strstr(cmd, ss) != NULL); |
955 | 0 | free(cmd); |
956 | 0 | return (retval); |
957 | 0 | } |
958 | 0 | return (0); |
959 | 0 | } |
960 | | |
961 | | static void |
962 | | window_tree_menu(void *modedata, struct client *c, key_code key) |
963 | 0 | { |
964 | 0 | struct window_tree_modedata *data = modedata; |
965 | 0 | struct window_pane *wp = data->wp; |
966 | 0 | struct window_mode_entry *wme; |
967 | |
|
968 | 0 | wme = TAILQ_FIRST(&wp->modes); |
969 | 0 | if (wme == NULL || wme->data != modedata) |
970 | 0 | return; |
971 | 0 | window_tree_key(wme, c, NULL, NULL, key, NULL); |
972 | 0 | } |
973 | | |
974 | | static key_code |
975 | | window_tree_get_key(void *modedata, void *itemdata, u_int line) |
976 | 0 | { |
977 | 0 | struct window_tree_modedata *data = modedata; |
978 | 0 | struct window_tree_itemdata *item = itemdata; |
979 | 0 | struct format_tree *ft; |
980 | 0 | struct session *s; |
981 | 0 | struct winlink *wl; |
982 | 0 | struct window_pane *wp; |
983 | 0 | char *expanded; |
984 | 0 | key_code key; |
985 | |
|
986 | 0 | ft = format_create(NULL, NULL, FORMAT_NONE, 0); |
987 | 0 | window_tree_pull_item(item, &s, &wl, &wp); |
988 | 0 | if (item->type == WINDOW_TREE_SESSION) |
989 | 0 | format_defaults(ft, NULL, s, NULL, NULL); |
990 | 0 | else if (item->type == WINDOW_TREE_WINDOW) |
991 | 0 | format_defaults(ft, NULL, s, wl, NULL); |
992 | 0 | else |
993 | 0 | format_defaults(ft, NULL, s, wl, wp); |
994 | 0 | format_add(ft, "line", "%u", line); |
995 | |
|
996 | 0 | expanded = format_expand(ft, data->key_format); |
997 | 0 | key = key_string_lookup_string(expanded); |
998 | 0 | free(expanded); |
999 | 0 | format_free(ft); |
1000 | 0 | return (key); |
1001 | 0 | } |
1002 | | |
1003 | | static int |
1004 | | window_tree_swap(void *cur_itemdata, void *other_itemdata, |
1005 | | struct sort_criteria *sort_crit) |
1006 | 0 | { |
1007 | 0 | struct window_tree_itemdata *cur = cur_itemdata; |
1008 | 0 | struct window_tree_itemdata *other = other_itemdata; |
1009 | 0 | struct session *cur_session, *other_session; |
1010 | 0 | struct winlink *cur_winlink, *other_winlink; |
1011 | 0 | struct window *cur_window, *other_window; |
1012 | 0 | struct window_pane *cur_pane, *other_pane; |
1013 | |
|
1014 | 0 | if (cur->type != other->type) |
1015 | 0 | return (0); |
1016 | 0 | if (cur->type != WINDOW_TREE_WINDOW) |
1017 | 0 | return (0); |
1018 | | |
1019 | 0 | window_tree_pull_item(cur, &cur_session, &cur_winlink, &cur_pane); |
1020 | 0 | window_tree_pull_item(other, &other_session, &other_winlink, |
1021 | 0 | &other_pane); |
1022 | |
|
1023 | 0 | if (cur_session != other_session) |
1024 | 0 | return (0); |
1025 | | |
1026 | | /* |
1027 | | * Swapping indexes would not swap positions in the tree, so prevent |
1028 | | * swapping to avoid confusing the user. |
1029 | | */ |
1030 | 0 | if (sort_would_window_tree_swap(sort_crit, cur_winlink, other_winlink)) |
1031 | 0 | return (0); |
1032 | | |
1033 | 0 | other_window = other_winlink->window; |
1034 | 0 | TAILQ_REMOVE(&other_window->winlinks, other_winlink, wentry); |
1035 | 0 | cur_window = cur_winlink->window; |
1036 | 0 | TAILQ_REMOVE(&cur_window->winlinks, cur_winlink, wentry); |
1037 | |
|
1038 | 0 | other_winlink->window = cur_window; |
1039 | 0 | TAILQ_INSERT_TAIL(&cur_window->winlinks, other_winlink, wentry); |
1040 | 0 | cur_winlink->window = other_window; |
1041 | 0 | TAILQ_INSERT_TAIL(&other_window->winlinks, cur_winlink, wentry); |
1042 | |
|
1043 | 0 | if (cur_session->curw == cur_winlink) |
1044 | 0 | session_set_current(cur_session, other_winlink); |
1045 | 0 | else if (cur_session->curw == other_winlink) |
1046 | 0 | session_set_current(cur_session, cur_winlink); |
1047 | 0 | session_group_synchronize_from(cur_session); |
1048 | 0 | server_redraw_session_group(cur_session); |
1049 | 0 | recalculate_sizes(); |
1050 | |
|
1051 | 0 | return (1); |
1052 | 0 | } |
1053 | | |
1054 | | static void |
1055 | | window_tree_sort(struct sort_criteria *sort_crit) |
1056 | 0 | { |
1057 | 0 | sort_crit->order_seq = window_tree_order_seq; |
1058 | 0 | if (sort_crit->order == SORT_END) |
1059 | 0 | sort_crit->order = sort_crit->order_seq[0]; |
1060 | 0 | } |
1061 | | |
1062 | | static const char* window_tree_help_lines[] = { |
1063 | | "#[fg=themelightgrey]" |
1064 | | " Enter #[#{E:tree-mode-border-style},acs]x#[default] Choose selected item", |
1065 | | "#[fg=themelightgrey]" |
1066 | | " S-Up #[#{E:tree-mode-border-style},acs]x#[default] Swap current and previous window", |
1067 | | "#[fg=themelightgrey]" |
1068 | | " S-Down #[#{E:tree-mode-border-style},acs]x#[default] Swap current and next window", |
1069 | | "#[fg=themelightgrey]" |
1070 | | " x #[#{E:tree-mode-border-style},acs]x#[default] Kill selected item", |
1071 | | "#[fg=themelightgrey]" |
1072 | | " X #[#{E:tree-mode-border-style},acs]x#[default] Kill tagged items", |
1073 | | "#[fg=themelightgrey]" |
1074 | | " < #[#{E:tree-mode-border-style},acs]x#[default] Scroll previews left", |
1075 | | "#[fg=themelightgrey]" |
1076 | | " > #[#{E:tree-mode-border-style},acs]x#[default] Scroll previews right", |
1077 | | "#[fg=themelightgrey]" |
1078 | | " m #[#{E:tree-mode-border-style},acs]x#[default] Set the marked pane", |
1079 | | "#[fg=themelightgrey]" |
1080 | | " M #[#{E:tree-mode-border-style},acs]x#[default] Clear the marked pane", |
1081 | | "#[fg=themelightgrey]" |
1082 | | " i #[#{E:tree-mode-border-style},acs]x#[default] Toggle session, window and pane information", |
1083 | | "#[fg=themelightgrey]" |
1084 | | " : #[#{E:tree-mode-border-style},acs]x#[default] Run a command for each tagged item", |
1085 | | "#[fg=themelightgrey]" |
1086 | | " f #[#{E:tree-mode-border-style},acs]x#[default] Enter a format", |
1087 | | "#[fg=themelightgrey]" |
1088 | | " H #[#{E:tree-mode-border-style},acs]x#[default] Jump to the starting pane", |
1089 | | NULL |
1090 | | }; |
1091 | | |
1092 | | static const char** |
1093 | | window_tree_help(u_int *width, const char **item) |
1094 | 0 | { |
1095 | 0 | *width = 51; |
1096 | 0 | *item = "item"; |
1097 | 0 | return (window_tree_help_lines); |
1098 | 0 | } |
1099 | | |
1100 | | static struct screen * |
1101 | | window_tree_init(struct window_mode_entry *wme, |
1102 | | __unused struct cmdq_item *item, struct cmd_find_state *fs, |
1103 | | struct args *args) |
1104 | 0 | { |
1105 | 0 | struct window_pane *wp = wme->wp; |
1106 | 0 | struct window_tree_modedata *data; |
1107 | 0 | struct screen *s; |
1108 | |
|
1109 | 0 | wme->data = data = xcalloc(1, sizeof *data); |
1110 | 0 | data->wp = wp; |
1111 | 0 | data->references = 1; |
1112 | |
|
1113 | 0 | if (args_has(args, 's')) |
1114 | 0 | data->type = WINDOW_TREE_SESSION; |
1115 | 0 | else if (args_has(args, 'w')) |
1116 | 0 | data->type = WINDOW_TREE_WINDOW; |
1117 | 0 | else |
1118 | 0 | data->type = WINDOW_TREE_PANE; |
1119 | 0 | memcpy(&data->fs, fs, sizeof data->fs); |
1120 | |
|
1121 | 0 | if (args == NULL || !args_has(args, 'F')) |
1122 | 0 | data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT); |
1123 | 0 | else |
1124 | 0 | data->format = xstrdup(args_get(args, 'F')); |
1125 | 0 | if (args == NULL || !args_has(args, 'K')) |
1126 | 0 | data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT); |
1127 | 0 | else |
1128 | 0 | data->key_format = xstrdup(args_get(args, 'K')); |
1129 | 0 | if (args == NULL || args_count(args) == 0) |
1130 | 0 | data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); |
1131 | 0 | else |
1132 | 0 | data->command = xstrdup(args_string(args, 0)); |
1133 | 0 | data->squash_groups = !args_has(args, 'G'); |
1134 | 0 | data->hide_preview_this_pane = args_has(args, 'h'); |
1135 | 0 | if (args_has(args, 'y')) |
1136 | 0 | data->prompt_flags = PROMPT_ACCEPT; |
1137 | |
|
1138 | 0 | data->data = mode_tree_start(wp, args, window_tree_build, |
1139 | 0 | window_tree_draw, window_tree_search, window_tree_menu, NULL, |
1140 | 0 | window_tree_get_key, window_tree_swap, window_tree_sort, |
1141 | 0 | window_tree_help, data, window_tree_menu_items, &s); |
1142 | 0 | mode_tree_zoom(data->data, args); |
1143 | 0 | mode_tree_view_name(data->data, "preview"); |
1144 | |
|
1145 | 0 | mode_tree_build(data->data); |
1146 | 0 | mode_tree_draw(data->data); |
1147 | |
|
1148 | 0 | data->type = WINDOW_TREE_NONE; |
1149 | |
|
1150 | 0 | return (s); |
1151 | 0 | } |
1152 | | |
1153 | | static void |
1154 | | window_tree_destroy(struct window_tree_modedata *data) |
1155 | 0 | { |
1156 | 0 | u_int i; |
1157 | |
|
1158 | 0 | if (--data->references != 0) |
1159 | 0 | return; |
1160 | | |
1161 | 0 | for (i = 0; i < data->item_size; i++) |
1162 | 0 | window_tree_free_item(data->item_list[i]); |
1163 | 0 | free(data->item_list); |
1164 | |
|
1165 | 0 | free(data->format); |
1166 | 0 | free(data->key_format); |
1167 | 0 | free(data->command); |
1168 | |
|
1169 | 0 | free(data); |
1170 | 0 | } |
1171 | | |
1172 | | static void |
1173 | | window_tree_free(struct window_mode_entry *wme) |
1174 | 0 | { |
1175 | 0 | struct window_tree_modedata *data = wme->data; |
1176 | |
|
1177 | 0 | if (data == NULL) |
1178 | 0 | return; |
1179 | | |
1180 | 0 | data->dead = 1; |
1181 | 0 | mode_tree_free(data->data); |
1182 | 0 | window_tree_destroy(data); |
1183 | 0 | } |
1184 | | |
1185 | | static void |
1186 | | window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy) |
1187 | 0 | { |
1188 | 0 | struct window_tree_modedata *data = wme->data; |
1189 | |
|
1190 | 0 | mode_tree_resize(data->data, sx, sy); |
1191 | 0 | } |
1192 | | |
1193 | | static void |
1194 | | window_tree_update(struct window_mode_entry *wme) |
1195 | 0 | { |
1196 | 0 | struct window_tree_modedata *data = wme->data; |
1197 | |
|
1198 | 0 | mode_tree_build(data->data); |
1199 | 0 | mode_tree_draw(data->data); |
1200 | 0 | data->wp->flags |= PANE_REDRAW; |
1201 | 0 | } |
1202 | | |
1203 | | static char * |
1204 | | window_tree_get_target(struct window_tree_itemdata *item, |
1205 | | struct cmd_find_state *fs) |
1206 | 0 | { |
1207 | 0 | struct session *s; |
1208 | 0 | struct winlink *wl; |
1209 | 0 | struct window_pane *wp; |
1210 | 0 | char *target; |
1211 | |
|
1212 | 0 | window_tree_pull_item(item, &s, &wl, &wp); |
1213 | |
|
1214 | 0 | target = NULL; |
1215 | 0 | switch (item->type) { |
1216 | 0 | case WINDOW_TREE_NONE: |
1217 | 0 | break; |
1218 | 0 | case WINDOW_TREE_SESSION: |
1219 | 0 | if (s == NULL) |
1220 | 0 | break; |
1221 | 0 | xasprintf(&target, "=%s:", s->name); |
1222 | 0 | break; |
1223 | 0 | case WINDOW_TREE_WINDOW: |
1224 | 0 | if (s == NULL || wl == NULL) |
1225 | 0 | break; |
1226 | 0 | xasprintf(&target, "=%s:%u.", s->name, wl->idx); |
1227 | 0 | break; |
1228 | 0 | case WINDOW_TREE_PANE: |
1229 | 0 | if (s == NULL || wl == NULL || wp == NULL) |
1230 | 0 | break; |
1231 | 0 | xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id); |
1232 | 0 | break; |
1233 | 0 | } |
1234 | 0 | if (target == NULL) |
1235 | 0 | cmd_find_clear_state(fs, 0); |
1236 | 0 | else |
1237 | 0 | cmd_find_from_winlink_pane(fs, wl, wp, 0); |
1238 | 0 | return (target); |
1239 | 0 | } |
1240 | | |
1241 | | static void |
1242 | | window_tree_command_each(void *modedata, void *itemdata, struct client *c, |
1243 | | __unused key_code key) |
1244 | 0 | { |
1245 | 0 | struct window_tree_modedata *data = modedata; |
1246 | 0 | struct window_tree_itemdata *item = itemdata; |
1247 | 0 | char *name; |
1248 | 0 | struct cmd_find_state fs; |
1249 | |
|
1250 | 0 | name = window_tree_get_target(item, &fs); |
1251 | 0 | if (name != NULL) |
1252 | 0 | mode_tree_run_command(c, &fs, data->entered, name); |
1253 | 0 | free(name); |
1254 | 0 | } |
1255 | | |
1256 | | static enum cmd_retval |
1257 | | window_tree_command_done(__unused struct cmdq_item *item, void *modedata) |
1258 | 0 | { |
1259 | 0 | struct window_tree_modedata *data = modedata; |
1260 | |
|
1261 | 0 | if (!data->dead) { |
1262 | 0 | mode_tree_build(data->data); |
1263 | 0 | mode_tree_draw(data->data); |
1264 | 0 | data->wp->flags |= PANE_REDRAW; |
1265 | 0 | } |
1266 | 0 | window_tree_destroy(data); |
1267 | 0 | return (CMD_RETURN_NORMAL); |
1268 | 0 | } |
1269 | | |
1270 | | static enum prompt_result |
1271 | | window_tree_command_callback(struct client *c, void *modedata, const char *s, |
1272 | | __unused enum prompt_key_result key) |
1273 | 0 | { |
1274 | 0 | struct window_tree_modedata *data = modedata; |
1275 | |
|
1276 | 0 | if (s == NULL || *s == '\0' || data->dead) |
1277 | 0 | return (PROMPT_CLOSE); |
1278 | | |
1279 | 0 | data->entered = s; |
1280 | 0 | mode_tree_each_tagged(data->data, window_tree_command_each, c, |
1281 | 0 | KEYC_NONE, 1); |
1282 | 0 | data->entered = NULL; |
1283 | |
|
1284 | 0 | data->references++; |
1285 | 0 | cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); |
1286 | |
|
1287 | 0 | return (PROMPT_CLOSE); |
1288 | 0 | } |
1289 | | |
1290 | | static void |
1291 | | window_tree_command_free(void *modedata) |
1292 | 0 | { |
1293 | 0 | struct window_tree_modedata *data = modedata; |
1294 | |
|
1295 | 0 | window_tree_destroy(data); |
1296 | 0 | } |
1297 | | |
1298 | | static void |
1299 | | window_tree_kill_each(__unused void *modedata, void *itemdata, |
1300 | | __unused struct client *c, __unused key_code key) |
1301 | 0 | { |
1302 | 0 | struct window_tree_itemdata *item = itemdata; |
1303 | 0 | struct session *s; |
1304 | 0 | struct winlink *wl; |
1305 | 0 | struct window_pane *wp; |
1306 | |
|
1307 | 0 | window_tree_pull_item(item, &s, &wl, &wp); |
1308 | |
|
1309 | 0 | switch (item->type) { |
1310 | 0 | case WINDOW_TREE_NONE: |
1311 | 0 | break; |
1312 | 0 | case WINDOW_TREE_SESSION: |
1313 | 0 | if (s != NULL) { |
1314 | 0 | server_destroy_session(s); |
1315 | 0 | session_destroy(s, 1, __func__); |
1316 | 0 | } |
1317 | 0 | break; |
1318 | 0 | case WINDOW_TREE_WINDOW: |
1319 | 0 | if (wl != NULL) |
1320 | 0 | server_kill_window(wl->window, 0); |
1321 | 0 | break; |
1322 | 0 | case WINDOW_TREE_PANE: |
1323 | 0 | if (wp != NULL) |
1324 | 0 | server_kill_pane(wp); |
1325 | 0 | break; |
1326 | 0 | } |
1327 | 0 | } |
1328 | | |
1329 | | static enum prompt_result |
1330 | | window_tree_kill_current_callback(struct client *c, void *modedata, |
1331 | | const char *s, __unused enum prompt_key_result key) |
1332 | 0 | { |
1333 | 0 | struct window_tree_modedata *data = modedata; |
1334 | 0 | struct mode_tree_data *mtd = data->data; |
1335 | |
|
1336 | 0 | if (s == NULL || *s == '\0' || data->dead) |
1337 | 0 | return (PROMPT_CLOSE); |
1338 | 0 | if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') |
1339 | 0 | return (PROMPT_CLOSE); |
1340 | | |
1341 | 0 | window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE); |
1342 | 0 | server_renumber_all(); |
1343 | |
|
1344 | 0 | data->references++; |
1345 | 0 | cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); |
1346 | |
|
1347 | 0 | return (PROMPT_CLOSE); |
1348 | 0 | } |
1349 | | |
1350 | | static enum prompt_result |
1351 | | window_tree_kill_tagged_callback(struct client *c, void *modedata, |
1352 | | const char *s, __unused enum prompt_key_result key) |
1353 | 0 | { |
1354 | 0 | struct window_tree_modedata *data = modedata; |
1355 | 0 | struct mode_tree_data *mtd = data->data; |
1356 | |
|
1357 | 0 | if (s == NULL || *s == '\0' || data->dead) |
1358 | 0 | return (PROMPT_CLOSE); |
1359 | 0 | if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') |
1360 | 0 | return (PROMPT_CLOSE); |
1361 | | |
1362 | 0 | mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1); |
1363 | 0 | server_renumber_all(); |
1364 | |
|
1365 | 0 | data->references++; |
1366 | 0 | cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); |
1367 | |
|
1368 | 0 | return (PROMPT_CLOSE); |
1369 | 0 | } |
1370 | | |
1371 | | static key_code |
1372 | | window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x, |
1373 | | struct window_tree_itemdata *item) |
1374 | 0 | { |
1375 | 0 | struct session *s; |
1376 | 0 | struct winlink *wl; |
1377 | 0 | struct window_pane *wp; |
1378 | 0 | u_int loop; |
1379 | |
|
1380 | 0 | if (key != KEYC_MOUSEDOWN1_PANE) |
1381 | 0 | return (KEYC_NONE); |
1382 | | |
1383 | 0 | if (data->left != -1 && x <= (u_int)data->left) |
1384 | 0 | return ('<'); |
1385 | 0 | if (data->right != -1 && x >= (u_int)data->right) |
1386 | 0 | return ('>'); |
1387 | | |
1388 | 0 | if (data->left != -1) |
1389 | 0 | x -= data->left; |
1390 | 0 | else if (x != 0) |
1391 | 0 | x--; |
1392 | 0 | if (x == 0 || data->end == 0) |
1393 | 0 | x = 0; |
1394 | 0 | else { |
1395 | 0 | x = x / data->each; |
1396 | 0 | if (data->start + x >= data->end) |
1397 | 0 | x = data->end - 1; |
1398 | 0 | } |
1399 | |
|
1400 | 0 | window_tree_pull_item(item, &s, &wl, &wp); |
1401 | 0 | if (item->type == WINDOW_TREE_SESSION) { |
1402 | 0 | if (s == NULL) |
1403 | 0 | return (KEYC_NONE); |
1404 | 0 | mode_tree_expand_current(data->data); |
1405 | 0 | loop = 0; |
1406 | 0 | RB_FOREACH(wl, winlinks, &s->windows) { |
1407 | 0 | if (loop == data->start + x) |
1408 | 0 | break; |
1409 | 0 | loop++; |
1410 | 0 | } |
1411 | 0 | if (wl != NULL) |
1412 | 0 | mode_tree_set_current(data->data, (uint64_t)wl); |
1413 | 0 | return ('\r'); |
1414 | 0 | } |
1415 | 0 | if (item->type == WINDOW_TREE_WINDOW) { |
1416 | 0 | if (wl == NULL) |
1417 | 0 | return (KEYC_NONE); |
1418 | 0 | mode_tree_expand_current(data->data); |
1419 | 0 | loop = 0; |
1420 | 0 | TAILQ_FOREACH(wp, &wl->window->panes, entry) { |
1421 | 0 | if (loop == data->start + x) |
1422 | 0 | break; |
1423 | 0 | loop++; |
1424 | 0 | } |
1425 | 0 | if (wp != NULL) |
1426 | 0 | mode_tree_set_current(data->data, (uint64_t)wp); |
1427 | 0 | return ('\r'); |
1428 | 0 | } |
1429 | 0 | return (KEYC_NONE); |
1430 | 0 | } |
1431 | | |
1432 | | static void |
1433 | | window_tree_key(struct window_mode_entry *wme, struct client *c, |
1434 | | __unused struct session *s, __unused struct winlink *wl, key_code key, |
1435 | | struct mouse_event *m) |
1436 | 0 | { |
1437 | 0 | struct window_pane *wp = wme->wp; |
1438 | 0 | struct window_tree_modedata *data = wme->data; |
1439 | 0 | struct window_tree_itemdata *item, *new_item; |
1440 | 0 | char *name, *prompt = NULL; |
1441 | 0 | struct cmd_find_state fs, *fsp = &data->fs; |
1442 | 0 | int finished; |
1443 | 0 | u_int tagged, x, y, idx; |
1444 | 0 | struct session *ns; |
1445 | 0 | struct winlink *nwl; |
1446 | 0 | struct window_pane *nwp; |
1447 | |
|
1448 | 0 | item = mode_tree_get_current(data->data); |
1449 | 0 | finished = mode_tree_key(data->data, c, &key, m, &x, &y); |
1450 | |
|
1451 | 0 | again: |
1452 | 0 | if (item != (new_item = mode_tree_get_current(data->data))) { |
1453 | 0 | item = new_item; |
1454 | 0 | data->offset = 0; |
1455 | 0 | } |
1456 | 0 | if (KEYC_IS_MOUSE(key) && m != NULL) { |
1457 | 0 | key = window_tree_mouse(data, key, x, item); |
1458 | 0 | goto again; |
1459 | 0 | } |
1460 | | |
1461 | 0 | switch (key) { |
1462 | 0 | case '<': |
1463 | 0 | data->offset--; |
1464 | 0 | break; |
1465 | 0 | case '>': |
1466 | 0 | data->offset++; |
1467 | 0 | break; |
1468 | 0 | case 'H': |
1469 | 0 | mode_tree_expand(data->data, (uint64_t)fsp->s); |
1470 | 0 | mode_tree_expand(data->data, (uint64_t)fsp->wl); |
1471 | 0 | if (!mode_tree_set_current(data->data, (uint64_t)wme->wp)) |
1472 | 0 | mode_tree_set_current(data->data, (uint64_t)fsp->wl); |
1473 | 0 | break; |
1474 | 0 | case 'm': |
1475 | 0 | window_tree_pull_item(item, &ns, &nwl, &nwp); |
1476 | 0 | server_set_marked(ns, nwl, nwp); |
1477 | 0 | mode_tree_build(data->data); |
1478 | 0 | break; |
1479 | 0 | case 'M': |
1480 | 0 | server_clear_marked(); |
1481 | 0 | mode_tree_build(data->data); |
1482 | 0 | break; |
1483 | 0 | case 'i': |
1484 | 0 | data->preview_is_info = !data->preview_is_info; |
1485 | 0 | if (data->preview_is_info) |
1486 | 0 | mode_tree_view_name(data->data, "info"); |
1487 | 0 | else |
1488 | 0 | mode_tree_view_name(data->data, "preview"); |
1489 | 0 | break; |
1490 | 0 | case 'x': |
1491 | 0 | window_tree_pull_item(item, &ns, &nwl, &nwp); |
1492 | 0 | switch (item->type) { |
1493 | 0 | case WINDOW_TREE_NONE: |
1494 | 0 | break; |
1495 | 0 | case WINDOW_TREE_SESSION: |
1496 | 0 | if (ns == NULL) |
1497 | 0 | break; |
1498 | 0 | xasprintf(&prompt, "Kill session %s? ", ns->name); |
1499 | 0 | break; |
1500 | 0 | case WINDOW_TREE_WINDOW: |
1501 | 0 | if (nwl == NULL) |
1502 | 0 | break; |
1503 | 0 | xasprintf(&prompt, "Kill window %u? ", nwl->idx); |
1504 | 0 | break; |
1505 | 0 | case WINDOW_TREE_PANE: |
1506 | 0 | if (nwp == NULL || window_pane_index(nwp, &idx) != 0) |
1507 | 0 | break; |
1508 | 0 | xasprintf(&prompt, "Kill pane %u? ", idx); |
1509 | 0 | break; |
1510 | 0 | } |
1511 | 0 | if (prompt == NULL) |
1512 | 0 | break; |
1513 | 0 | data->references++; |
1514 | 0 | mode_tree_set_prompt(data->data, c, prompt, "", |
1515 | 0 | PROMPT_TYPE_COMMAND, |
1516 | 0 | PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags, |
1517 | 0 | window_tree_kill_current_callback, window_tree_command_free, |
1518 | 0 | data); |
1519 | 0 | free(prompt); |
1520 | 0 | break; |
1521 | 0 | case 'X': |
1522 | 0 | tagged = mode_tree_count_tagged(data->data); |
1523 | 0 | if (tagged == 0) |
1524 | 0 | break; |
1525 | 0 | xasprintf(&prompt, "Kill %u tagged? ", tagged); |
1526 | 0 | data->references++; |
1527 | 0 | mode_tree_set_prompt(data->data, c, prompt, "", |
1528 | 0 | PROMPT_TYPE_COMMAND, |
1529 | 0 | PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags, |
1530 | 0 | window_tree_kill_tagged_callback, window_tree_command_free, |
1531 | 0 | data); |
1532 | 0 | free(prompt); |
1533 | 0 | break; |
1534 | 0 | case ':': |
1535 | 0 | tagged = mode_tree_count_tagged(data->data); |
1536 | 0 | if (tagged != 0) |
1537 | 0 | xasprintf(&prompt, "(%u tagged) ", tagged); |
1538 | 0 | else |
1539 | 0 | xasprintf(&prompt, "(current) "); |
1540 | 0 | data->references++; |
1541 | 0 | mode_tree_set_prompt(data->data, c, prompt, "", |
1542 | 0 | PROMPT_TYPE_COMMAND, PROMPT_NOFORMAT, |
1543 | 0 | window_tree_command_callback, window_tree_command_free, |
1544 | 0 | data); |
1545 | 0 | free(prompt); |
1546 | 0 | break; |
1547 | 0 | case '\r': |
1548 | 0 | name = window_tree_get_target(item, &fs); |
1549 | 0 | if (name != NULL) |
1550 | 0 | mode_tree_run_command(c, NULL, data->command, name); |
1551 | 0 | finished = 1; |
1552 | 0 | free(name); |
1553 | 0 | break; |
1554 | 0 | } |
1555 | 0 | if (finished) |
1556 | 0 | window_pane_reset_mode(wp); |
1557 | 0 | else { |
1558 | 0 | mode_tree_draw(data->data); |
1559 | 0 | wp->flags |= PANE_REDRAW; |
1560 | 0 | } |
1561 | 0 | } |