window-tree.c revision 1.2 1 1.1 christos /* $OpenBSD$ */
2 1.1 christos
3 1.1 christos /*
4 1.1 christos * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott (at) gmail.com>
5 1.1 christos *
6 1.1 christos * Permission to use, copy, modify, and distribute this software for any
7 1.1 christos * purpose with or without fee is hereby granted, provided that the above
8 1.1 christos * copyright notice and this permission notice appear in all copies.
9 1.1 christos *
10 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 1.1 christos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 1.1 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 1.1 christos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 1.1 christos * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 1.1 christos * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 1.1 christos * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 1.1 christos */
18 1.1 christos
19 1.1 christos #include <sys/types.h>
20 1.1 christos
21 1.1 christos #include <stdlib.h>
22 1.1 christos #include <string.h>
23 1.1 christos
24 1.1 christos #include "tmux.h"
25 1.1 christos
26 1.1 christos static struct screen *window_tree_init(struct window_pane *,
27 1.1 christos struct cmd_find_state *, struct args *);
28 1.1 christos static void window_tree_free(struct window_pane *);
29 1.1 christos static void window_tree_resize(struct window_pane *, u_int, u_int);
30 1.1 christos static void window_tree_key(struct window_pane *,
31 1.1 christos struct client *, struct session *, key_code,
32 1.1 christos struct mouse_event *);
33 1.1 christos
34 1.1 christos #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'"
35 1.1 christos
36 1.1 christos #define WINDOW_TREE_DEFAULT_FORMAT \
37 1.1 christos "#{?pane_format," \
38 1.1 christos "#{pane_current_command} \"#{pane_title}\"" \
39 1.1 christos "," \
40 1.1 christos "#{?window_format," \
41 1.1 christos "#{window_name}#{window_flags} " \
42 1.1 christos "(#{window_panes} panes)" \
43 1.1 christos "#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \
44 1.1 christos "," \
45 1.1 christos "#{session_windows} windows" \
46 1.1 christos "#{?session_grouped, (group ,}" \
47 1.1 christos "#{session_group}#{?session_grouped,),}" \
48 1.1 christos "#{?session_attached, (attached),}" \
49 1.1 christos "}" \
50 1.1 christos "}"
51 1.1 christos
52 1.1 christos const struct window_mode window_tree_mode = {
53 1.1 christos .name = "tree-mode",
54 1.1 christos
55 1.1 christos .init = window_tree_init,
56 1.1 christos .free = window_tree_free,
57 1.1 christos .resize = window_tree_resize,
58 1.1 christos .key = window_tree_key,
59 1.1 christos };
60 1.1 christos
61 1.1 christos enum window_tree_sort_type {
62 1.1 christos WINDOW_TREE_BY_INDEX,
63 1.1 christos WINDOW_TREE_BY_NAME,
64 1.1 christos WINDOW_TREE_BY_TIME,
65 1.1 christos };
66 1.1 christos static const char *window_tree_sort_list[] = {
67 1.1 christos "index",
68 1.1 christos "name",
69 1.1 christos "time"
70 1.1 christos };
71 1.1 christos
72 1.1 christos enum window_tree_type {
73 1.1 christos WINDOW_TREE_NONE,
74 1.1 christos WINDOW_TREE_SESSION,
75 1.1 christos WINDOW_TREE_WINDOW,
76 1.1 christos WINDOW_TREE_PANE,
77 1.1 christos };
78 1.1 christos
79 1.1 christos struct window_tree_itemdata {
80 1.1 christos enum window_tree_type type;
81 1.1 christos int session;
82 1.1 christos int winlink;
83 1.1 christos int pane;
84 1.1 christos };
85 1.1 christos
86 1.1 christos struct window_tree_modedata {
87 1.1 christos struct window_pane *wp;
88 1.1 christos int dead;
89 1.1 christos int references;
90 1.1 christos
91 1.1 christos struct mode_tree_data *data;
92 1.1 christos char *format;
93 1.1 christos char *command;
94 1.1 christos
95 1.1 christos struct window_tree_itemdata **item_list;
96 1.1 christos u_int item_size;
97 1.1 christos
98 1.1 christos struct client *client;
99 1.1 christos const char *entered;
100 1.1 christos
101 1.1 christos struct cmd_find_state fs;
102 1.1 christos enum window_tree_type type;
103 1.1 christos
104 1.1 christos int offset;
105 1.1 christos };
106 1.1 christos
107 1.1 christos static void
108 1.1 christos window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
109 1.1 christos struct winlink **wlp, struct window_pane **wp)
110 1.1 christos {
111 1.1 christos *wp = NULL;
112 1.1 christos *wlp = NULL;
113 1.1 christos *sp = session_find_by_id(item->session);
114 1.1 christos if (*sp == NULL)
115 1.1 christos return;
116 1.1 christos if (item->type == WINDOW_TREE_SESSION) {
117 1.1 christos *wlp = (*sp)->curw;
118 1.1 christos *wp = (*wlp)->window->active;
119 1.1 christos return;
120 1.1 christos }
121 1.1 christos
122 1.1 christos *wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
123 1.1 christos if (*wlp == NULL) {
124 1.1 christos *sp = NULL;
125 1.1 christos return;
126 1.1 christos }
127 1.1 christos if (item->type == WINDOW_TREE_WINDOW) {
128 1.1 christos *wp = (*wlp)->window->active;
129 1.1 christos return;
130 1.1 christos }
131 1.1 christos
132 1.1 christos *wp = window_pane_find_by_id(item->pane);
133 1.1 christos if (!window_has_pane((*wlp)->window, *wp))
134 1.1 christos *wp = NULL;
135 1.1 christos if (*wp == NULL) {
136 1.1 christos *sp = NULL;
137 1.1 christos *wlp = NULL;
138 1.1 christos return;
139 1.1 christos }
140 1.1 christos }
141 1.1 christos
142 1.1 christos static struct window_tree_itemdata *
143 1.1 christos window_tree_add_item(struct window_tree_modedata *data)
144 1.1 christos {
145 1.1 christos struct window_tree_itemdata *item;
146 1.1 christos
147 1.1 christos data->item_list = xreallocarray(data->item_list, data->item_size + 1,
148 1.1 christos sizeof *data->item_list);
149 1.1 christos item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
150 1.1 christos return (item);
151 1.1 christos }
152 1.1 christos
153 1.1 christos static void
154 1.1 christos window_tree_free_item(struct window_tree_itemdata *item)
155 1.1 christos {
156 1.1 christos free(item);
157 1.1 christos }
158 1.1 christos
159 1.1 christos static int
160 1.1 christos window_tree_cmp_session_name(const void *a0, const void *b0)
161 1.1 christos {
162 1.1 christos const struct session *const *a = a0;
163 1.1 christos const struct session *const *b = b0;
164 1.1 christos
165 1.1 christos return (strcmp((*a)->name, (*b)->name));
166 1.1 christos }
167 1.1 christos
168 1.1 christos static int
169 1.1 christos window_tree_cmp_session_time(const void *a0, const void *b0)
170 1.1 christos {
171 1.1 christos const struct session *const *a = a0;
172 1.1 christos const struct session *const *b = b0;
173 1.1 christos
174 1.1 christos if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >))
175 1.1 christos return (-1);
176 1.1 christos if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <))
177 1.1 christos return (1);
178 1.1 christos return (strcmp((*a)->name, (*b)->name));
179 1.1 christos }
180 1.1 christos
181 1.1 christos static int
182 1.1 christos window_tree_cmp_window_name(const void *a0, const void *b0)
183 1.1 christos {
184 1.1 christos const struct winlink *const *a = a0;
185 1.1 christos const struct winlink *const *b = b0;
186 1.1 christos
187 1.1 christos return (strcmp((*a)->window->name, (*b)->window->name));
188 1.1 christos }
189 1.1 christos
190 1.1 christos static int
191 1.1 christos window_tree_cmp_window_time(const void *a0, const void *b0)
192 1.1 christos {
193 1.1 christos const struct winlink *const *a = a0;
194 1.1 christos const struct winlink *const *b = b0;
195 1.1 christos
196 1.1 christos if (timercmp(&(*a)->window->activity_time,
197 1.1 christos &(*b)->window->activity_time, >))
198 1.1 christos return (-1);
199 1.1 christos if (timercmp(&(*a)->window->activity_time,
200 1.1 christos &(*b)->window->activity_time, <))
201 1.1 christos return (1);
202 1.1 christos return (strcmp((*a)->window->name, (*b)->window->name));
203 1.1 christos }
204 1.1 christos
205 1.1 christos static int
206 1.1 christos window_tree_cmp_pane_time(const void *a0, const void *b0)
207 1.1 christos {
208 1.1 christos const struct window_pane *const *a = a0;
209 1.1 christos const struct window_pane *const *b = b0;
210 1.1 christos
211 1.1 christos if ((*a)->active_point < (*b)->active_point)
212 1.1 christos return (-1);
213 1.1 christos if ((*a)->active_point > (*b)->active_point)
214 1.1 christos return (1);
215 1.1 christos return (0);
216 1.1 christos }
217 1.1 christos
218 1.1 christos static void
219 1.1 christos window_tree_build_pane(struct session *s, struct winlink *wl,
220 1.1 christos struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
221 1.1 christos {
222 1.1 christos struct window_tree_modedata *data = modedata;
223 1.1 christos struct window_tree_itemdata *item;
224 1.1 christos char *name, *text;
225 1.1 christos u_int idx;
226 1.1 christos
227 1.1 christos window_pane_index(wp, &idx);
228 1.1 christos
229 1.1 christos item = window_tree_add_item(data);
230 1.1 christos item->type = WINDOW_TREE_PANE;
231 1.1 christos item->session = s->id;
232 1.1 christos item->winlink = wl->idx;
233 1.1 christos item->pane = wp->id;
234 1.1 christos
235 1.1 christos text = format_single(NULL, data->format, NULL, s, wl, wp);
236 1.1 christos xasprintf(&name, "%u", idx);
237 1.1 christos
238 1.2 kre mode_tree_add(data->data, parent, item, (uintptr_t)wp, name, text, -1);
239 1.1 christos free(text);
240 1.1 christos free(name);
241 1.1 christos }
242 1.1 christos
243 1.1 christos static int
244 1.1 christos window_tree_filter_pane(struct session *s, struct winlink *wl,
245 1.1 christos struct window_pane *wp, const char *filter)
246 1.1 christos {
247 1.1 christos char *cp;
248 1.1 christos int result;
249 1.1 christos
250 1.1 christos if (filter == NULL)
251 1.1 christos return (1);
252 1.1 christos
253 1.1 christos cp = format_single(NULL, filter, NULL, s, wl, wp);
254 1.1 christos result = format_true(cp);
255 1.1 christos free(cp);
256 1.1 christos
257 1.1 christos return (result);
258 1.1 christos }
259 1.1 christos
260 1.1 christos static int
261 1.1 christos window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
262 1.1 christos u_int sort_type, struct mode_tree_item *parent, const char *filter)
263 1.1 christos {
264 1.1 christos struct window_tree_modedata *data = modedata;
265 1.1 christos struct window_tree_itemdata *item;
266 1.1 christos struct mode_tree_item *mti;
267 1.1 christos char *name, *text;
268 1.1 christos struct window_pane *wp, **l;
269 1.1 christos u_int n, i;
270 1.1 christos int expanded;
271 1.1 christos
272 1.1 christos item = window_tree_add_item(data);
273 1.1 christos item->type = WINDOW_TREE_WINDOW;
274 1.1 christos item->session = s->id;
275 1.1 christos item->winlink = wl->idx;
276 1.1 christos item->pane = -1;
277 1.1 christos
278 1.1 christos text = format_single(NULL, data->format, NULL, s, wl, NULL);
279 1.1 christos xasprintf(&name, "%u", wl->idx);
280 1.1 christos
281 1.1 christos if (data->type == WINDOW_TREE_SESSION ||
282 1.1 christos data->type == WINDOW_TREE_WINDOW)
283 1.1 christos expanded = 0;
284 1.1 christos else
285 1.1 christos expanded = 1;
286 1.2 kre mti = mode_tree_add(data->data, parent, item, (uintptr_t)wl, name, text,
287 1.1 christos expanded);
288 1.1 christos free(text);
289 1.1 christos free(name);
290 1.1 christos
291 1.1 christos wp = TAILQ_FIRST(&wl->window->panes);
292 1.1 christos if (TAILQ_NEXT(wp, entry) == NULL) {
293 1.1 christos if (!window_tree_filter_pane(s, wl, wp, filter))
294 1.1 christos goto empty;
295 1.1 christos return (1);
296 1.1 christos }
297 1.1 christos
298 1.1 christos l = NULL;
299 1.1 christos n = 0;
300 1.1 christos
301 1.1 christos TAILQ_FOREACH(wp, &wl->window->panes, entry) {
302 1.1 christos if (!window_tree_filter_pane(s, wl, wp, filter))
303 1.1 christos continue;
304 1.1 christos l = xreallocarray(l, n + 1, sizeof *l);
305 1.1 christos l[n++] = wp;
306 1.1 christos }
307 1.1 christos if (n == 0)
308 1.1 christos goto empty;
309 1.1 christos
310 1.1 christos switch (sort_type) {
311 1.1 christos case WINDOW_TREE_BY_INDEX:
312 1.1 christos break;
313 1.1 christos case WINDOW_TREE_BY_NAME:
314 1.1 christos /* Panes don't have names, so leave in number order. */
315 1.1 christos break;
316 1.1 christos case WINDOW_TREE_BY_TIME:
317 1.1 christos qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
318 1.1 christos break;
319 1.1 christos }
320 1.1 christos
321 1.1 christos for (i = 0; i < n; i++)
322 1.1 christos window_tree_build_pane(s, wl, l[i], modedata, mti);
323 1.1 christos free(l);
324 1.1 christos return (1);
325 1.1 christos
326 1.1 christos empty:
327 1.1 christos window_tree_free_item(item);
328 1.1 christos data->item_size--;
329 1.1 christos mode_tree_remove(data->data, mti);
330 1.1 christos return (0);
331 1.1 christos }
332 1.1 christos
333 1.1 christos static void
334 1.1 christos window_tree_build_session(struct session *s, void* modedata,
335 1.1 christos u_int sort_type, const char *filter)
336 1.1 christos {
337 1.1 christos struct window_tree_modedata *data = modedata;
338 1.1 christos struct window_tree_itemdata *item;
339 1.1 christos struct mode_tree_item *mti;
340 1.1 christos char *text;
341 1.1 christos struct winlink *wl, **l;
342 1.1 christos u_int n, i, empty;
343 1.1 christos int expanded;
344 1.1 christos
345 1.1 christos item = window_tree_add_item(data);
346 1.1 christos item->type = WINDOW_TREE_SESSION;
347 1.1 christos item->session = s->id;
348 1.1 christos item->winlink = -1;
349 1.1 christos item->pane = -1;
350 1.1 christos
351 1.1 christos text = format_single(NULL, data->format, NULL, s, NULL, NULL);
352 1.1 christos
353 1.1 christos if (data->type == WINDOW_TREE_SESSION)
354 1.1 christos expanded = 0;
355 1.1 christos else
356 1.1 christos expanded = 1;
357 1.2 kre mti = mode_tree_add(data->data, NULL, item, (uintptr_t)s, s->name, text,
358 1.1 christos expanded);
359 1.1 christos free(text);
360 1.1 christos
361 1.1 christos l = NULL;
362 1.1 christos n = 0;
363 1.1 christos RB_FOREACH(wl, winlinks, &s->windows) {
364 1.1 christos l = xreallocarray(l, n + 1, sizeof *l);
365 1.1 christos l[n++] = wl;
366 1.1 christos }
367 1.1 christos switch (sort_type) {
368 1.1 christos case WINDOW_TREE_BY_INDEX:
369 1.1 christos break;
370 1.1 christos case WINDOW_TREE_BY_NAME:
371 1.1 christos qsort(l, n, sizeof *l, window_tree_cmp_window_name);
372 1.1 christos break;
373 1.1 christos case WINDOW_TREE_BY_TIME:
374 1.1 christos qsort(l, n, sizeof *l, window_tree_cmp_window_time);
375 1.1 christos break;
376 1.1 christos }
377 1.1 christos
378 1.1 christos empty = 0;
379 1.1 christos for (i = 0; i < n; i++) {
380 1.1 christos if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
381 1.1 christos filter))
382 1.1 christos empty++;
383 1.1 christos }
384 1.1 christos if (empty == n) {
385 1.1 christos window_tree_free_item(item);
386 1.1 christos data->item_size--;
387 1.1 christos mode_tree_remove(data->data, mti);
388 1.1 christos }
389 1.1 christos free(l);
390 1.1 christos }
391 1.1 christos
392 1.1 christos static void
393 1.1 christos window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
394 1.1 christos const char *filter)
395 1.1 christos {
396 1.1 christos struct window_tree_modedata *data = modedata;
397 1.1 christos struct session *s, **l;
398 1.1 christos u_int n, i;
399 1.1 christos
400 1.1 christos for (i = 0; i < data->item_size; i++)
401 1.1 christos window_tree_free_item(data->item_list[i]);
402 1.1 christos free(data->item_list);
403 1.1 christos data->item_list = NULL;
404 1.1 christos data->item_size = 0;
405 1.1 christos
406 1.1 christos l = NULL;
407 1.1 christos n = 0;
408 1.1 christos RB_FOREACH(s, sessions, &sessions) {
409 1.1 christos l = xreallocarray(l, n + 1, sizeof *l);
410 1.1 christos l[n++] = s;
411 1.1 christos }
412 1.1 christos switch (sort_type) {
413 1.1 christos case WINDOW_TREE_BY_INDEX:
414 1.1 christos break;
415 1.1 christos case WINDOW_TREE_BY_NAME:
416 1.1 christos qsort(l, n, sizeof *l, window_tree_cmp_session_name);
417 1.1 christos break;
418 1.1 christos case WINDOW_TREE_BY_TIME:
419 1.1 christos qsort(l, n, sizeof *l, window_tree_cmp_session_time);
420 1.1 christos break;
421 1.1 christos }
422 1.1 christos
423 1.1 christos for (i = 0; i < n; i++)
424 1.1 christos window_tree_build_session(l[i], modedata, sort_type, filter);
425 1.1 christos free(l);
426 1.1 christos
427 1.1 christos switch (data->type) {
428 1.1 christos case WINDOW_TREE_NONE:
429 1.1 christos break;
430 1.1 christos case WINDOW_TREE_SESSION:
431 1.2 kre *tag = (uintptr_t)data->fs.s;
432 1.1 christos break;
433 1.1 christos case WINDOW_TREE_WINDOW:
434 1.2 kre *tag = (uintptr_t)data->fs.wl;
435 1.1 christos break;
436 1.1 christos case WINDOW_TREE_PANE:
437 1.2 kre *tag = (uintptr_t)data->fs.wp;
438 1.1 christos break;
439 1.1 christos }
440 1.1 christos }
441 1.1 christos
442 1.1 christos static void
443 1.1 christos window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
444 1.1 christos struct screen_write_ctx *ctx, u_int sx, u_int sy)
445 1.1 christos {
446 1.1 christos struct options *oo = s->options;
447 1.1 christos struct winlink *wl;
448 1.1 christos struct window *w;
449 1.1 christos u_int loop, total, visible, each, width, offset;
450 1.1 christos u_int current, start, end, remaining, i;
451 1.1 christos struct grid_cell gc;
452 1.1 christos int colour, active_colour, left, right;
453 1.1 christos char *label;
454 1.1 christos size_t len;
455 1.1 christos
456 1.1 christos total = winlink_count(&s->windows);
457 1.1 christos
458 1.1 christos memcpy(&gc, &grid_default_cell, sizeof gc);
459 1.1 christos colour = options_get_number(oo, "display-panes-colour");
460 1.1 christos active_colour = options_get_number(oo, "display-panes-active-colour");
461 1.1 christos
462 1.1 christos if (sx / total < 24) {
463 1.1 christos visible = sx / 24;
464 1.1 christos if (visible == 0)
465 1.1 christos visible = 1;
466 1.1 christos } else
467 1.1 christos visible = total;
468 1.1 christos
469 1.1 christos current = 0;
470 1.1 christos RB_FOREACH(wl, winlinks, &s->windows) {
471 1.1 christos if (wl == s->curw)
472 1.1 christos break;
473 1.1 christos current++;
474 1.1 christos }
475 1.1 christos
476 1.1 christos if (current < visible) {
477 1.1 christos start = 0;
478 1.1 christos end = visible;
479 1.1 christos } else if (current >= total - visible) {
480 1.1 christos start = total - visible;
481 1.1 christos end = total;
482 1.1 christos } else {
483 1.1 christos start = current - (visible / 2);
484 1.1 christos end = start + visible;
485 1.1 christos }
486 1.1 christos
487 1.1 christos if (data->offset < -(int)start)
488 1.1 christos data->offset = -(int)start;
489 1.1 christos if (data->offset > (int)(total - end))
490 1.1 christos data->offset = (int)(total - end);
491 1.1 christos start += data->offset;
492 1.1 christos end += data->offset;
493 1.1 christos
494 1.1 christos left = (start != 0);
495 1.1 christos right = (end != total);
496 1.1 christos if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
497 1.1 christos left = right = 0;
498 1.1 christos if (left && right) {
499 1.1 christos each = (sx - 6) / visible;
500 1.1 christos remaining = (sx - 6) - (visible * each);
501 1.1 christos } else if (left || right) {
502 1.1 christos each = (sx - 3) / visible;
503 1.1 christos remaining = (sx - 3) - (visible * each);
504 1.1 christos } else {
505 1.1 christos each = sx / visible;
506 1.1 christos remaining = sx - (visible * each);
507 1.1 christos }
508 1.1 christos if (each == 0)
509 1.1 christos return;
510 1.1 christos
511 1.1 christos if (left) {
512 1.1 christos screen_write_cursormove(ctx, 2, 0);
513 1.1 christos screen_write_vline(ctx, sy, 0, 0);
514 1.1 christos screen_write_cursormove(ctx, 0, sy / 2);
515 1.1 christos screen_write_puts(ctx, &grid_default_cell, "<");
516 1.1 christos }
517 1.1 christos if (right) {
518 1.1 christos screen_write_cursormove(ctx, sx - 3, 0);
519 1.1 christos screen_write_vline(ctx, sy, 0, 0);
520 1.1 christos screen_write_cursormove(ctx, sx - 1, sy / 2);
521 1.1 christos screen_write_puts(ctx, &grid_default_cell, ">");
522 1.1 christos }
523 1.1 christos
524 1.1 christos i = loop = 0;
525 1.1 christos RB_FOREACH(wl, winlinks, &s->windows) {
526 1.1 christos if (loop == end)
527 1.1 christos break;
528 1.1 christos if (loop < start) {
529 1.1 christos loop++;
530 1.1 christos continue;
531 1.1 christos }
532 1.1 christos w = wl->window;
533 1.1 christos
534 1.1 christos if (wl == s->curw)
535 1.1 christos gc.fg = active_colour;
536 1.1 christos else
537 1.1 christos gc.fg = colour;
538 1.1 christos
539 1.1 christos if (left)
540 1.1 christos offset = 3 + (i * each);
541 1.1 christos else
542 1.1 christos offset = (i * each);
543 1.1 christos if (loop == end - 1)
544 1.1 christos width = each + remaining;
545 1.1 christos else
546 1.1 christos width = each - 1;
547 1.1 christos
548 1.1 christos screen_write_cursormove(ctx, offset, 0);
549 1.1 christos screen_write_preview(ctx, &w->active->base, width, sy);
550 1.1 christos
551 1.1 christos xasprintf(&label, " %u:%s ", wl->idx, w->name);
552 1.1 christos if (strlen(label) > width)
553 1.1 christos xasprintf(&label, " %u ", wl->idx);
554 1.1 christos len = strlen(label) / 2;
555 1.1 christos screen_write_cursormove(ctx, offset + (each / 2) - len, sy / 2);
556 1.1 christos if (len < width)
557 1.1 christos screen_write_puts(ctx, &gc, "%s", label);
558 1.1 christos free(label);
559 1.1 christos
560 1.1 christos if (loop != end - 1) {
561 1.1 christos screen_write_cursormove(ctx, offset + width, 0);
562 1.1 christos screen_write_vline(ctx, sy, 0, 0);
563 1.1 christos }
564 1.1 christos loop++;
565 1.1 christos
566 1.1 christos i++;
567 1.1 christos }
568 1.1 christos }
569 1.1 christos
570 1.1 christos static void
571 1.1 christos window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
572 1.1 christos struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy)
573 1.1 christos {
574 1.1 christos struct options *oo = s->options;
575 1.1 christos struct window_pane *wp;
576 1.1 christos u_int loop, total, visible, each, width, offset;
577 1.1 christos u_int current, start, end, remaining, i;
578 1.1 christos struct grid_cell gc;
579 1.1 christos int colour, active_colour, left, right;
580 1.1 christos char *label;
581 1.1 christos size_t len;
582 1.1 christos
583 1.1 christos total = window_count_panes(w);
584 1.1 christos
585 1.1 christos memcpy(&gc, &grid_default_cell, sizeof gc);
586 1.1 christos colour = options_get_number(oo, "display-panes-colour");
587 1.1 christos active_colour = options_get_number(oo, "display-panes-active-colour");
588 1.1 christos
589 1.1 christos if (sx / total < 24) {
590 1.1 christos visible = sx / 24;
591 1.1 christos if (visible == 0)
592 1.1 christos visible = 1;
593 1.1 christos } else
594 1.1 christos visible = total;
595 1.1 christos
596 1.1 christos current = 0;
597 1.1 christos TAILQ_FOREACH(wp, &w->panes, entry) {
598 1.1 christos if (wp == w->active)
599 1.1 christos break;
600 1.1 christos current++;
601 1.1 christos }
602 1.1 christos
603 1.1 christos if (current < visible) {
604 1.1 christos start = 0;
605 1.1 christos end = visible;
606 1.1 christos } else if (current >= total - visible) {
607 1.1 christos start = total - visible;
608 1.1 christos end = total;
609 1.1 christos } else {
610 1.1 christos start = current - (visible / 2);
611 1.1 christos end = start + visible;
612 1.1 christos }
613 1.1 christos
614 1.1 christos if (data->offset < -(int)start)
615 1.1 christos data->offset = -(int)start;
616 1.1 christos if (data->offset > (int)(total - end))
617 1.1 christos data->offset = (int)(total - end);
618 1.1 christos start += data->offset;
619 1.1 christos end += data->offset;
620 1.1 christos
621 1.1 christos left = (start != 0);
622 1.1 christos right = (end != total);
623 1.1 christos if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
624 1.1 christos left = right = 0;
625 1.1 christos if (left && right) {
626 1.1 christos each = (sx - 6) / visible;
627 1.1 christos remaining = (sx - 6) - (visible * each);
628 1.1 christos } else if (left || right) {
629 1.1 christos each = (sx - 3) / visible;
630 1.1 christos remaining = (sx - 3) - (visible * each);
631 1.1 christos } else {
632 1.1 christos each = sx / visible;
633 1.1 christos remaining = sx - (visible * each);
634 1.1 christos }
635 1.1 christos if (each == 0)
636 1.1 christos return;
637 1.1 christos
638 1.1 christos if (left) {
639 1.1 christos screen_write_cursormove(ctx, 2, 0);
640 1.1 christos screen_write_vline(ctx, sy, 0, 0);
641 1.1 christos screen_write_cursormove(ctx, 0, sy / 2);
642 1.1 christos screen_write_puts(ctx, &grid_default_cell, "<");
643 1.1 christos }
644 1.1 christos if (right) {
645 1.1 christos screen_write_cursormove(ctx, sx - 3, 0);
646 1.1 christos screen_write_vline(ctx, sy, 0, 0);
647 1.1 christos screen_write_cursormove(ctx, sx - 1, sy / 2);
648 1.1 christos screen_write_puts(ctx, &grid_default_cell, ">");
649 1.1 christos }
650 1.1 christos
651 1.1 christos i = loop = 0;
652 1.1 christos TAILQ_FOREACH(wp, &w->panes, entry) {
653 1.1 christos if (loop == end)
654 1.1 christos break;
655 1.1 christos if (loop < start) {
656 1.1 christos loop++;
657 1.1 christos continue;
658 1.1 christos }
659 1.1 christos
660 1.1 christos if (wp == w->active)
661 1.1 christos gc.fg = active_colour;
662 1.1 christos else
663 1.1 christos gc.fg = colour;
664 1.1 christos
665 1.1 christos if (left)
666 1.1 christos offset = 3 + (i * each);
667 1.1 christos else
668 1.1 christos offset = (i * each);
669 1.1 christos if (loop == end - 1)
670 1.1 christos width = each + remaining;
671 1.1 christos else
672 1.1 christos width = each - 1;
673 1.1 christos
674 1.1 christos screen_write_cursormove(ctx, offset, 0);
675 1.1 christos screen_write_preview(ctx, &wp->base, width, sy);
676 1.1 christos
677 1.1 christos xasprintf(&label, " %u ", loop);
678 1.1 christos len = strlen(label) / 2;
679 1.1 christos screen_write_cursormove(ctx, offset + (each / 2) - len, sy / 2);
680 1.1 christos if (len < width)
681 1.1 christos screen_write_puts(ctx, &gc, "%s", label);
682 1.1 christos free(label);
683 1.1 christos
684 1.1 christos if (loop != end - 1) {
685 1.1 christos screen_write_cursormove(ctx, offset + width, 0);
686 1.1 christos screen_write_vline(ctx, sy, 0, 0);
687 1.1 christos }
688 1.1 christos loop++;
689 1.1 christos
690 1.1 christos i++;
691 1.1 christos }
692 1.1 christos }
693 1.1 christos
694 1.1 christos static struct screen *
695 1.1 christos window_tree_draw(void *modedata, void *itemdata, u_int sx, u_int sy)
696 1.1 christos {
697 1.1 christos struct window_tree_itemdata *item = itemdata;
698 1.1 christos struct session *sp;
699 1.1 christos struct winlink *wlp;
700 1.1 christos struct window_pane *wp;
701 1.1 christos static struct screen s;
702 1.1 christos struct screen_write_ctx ctx;
703 1.1 christos
704 1.1 christos window_tree_pull_item(item, &sp, &wlp, &wp);
705 1.1 christos if (wp == NULL)
706 1.1 christos return (NULL);
707 1.1 christos
708 1.1 christos screen_init(&s, sx, sy, 0);
709 1.1 christos screen_write_start(&ctx, NULL, &s);
710 1.1 christos
711 1.1 christos switch (item->type) {
712 1.1 christos case WINDOW_TREE_NONE:
713 1.1 christos return (0);
714 1.1 christos case WINDOW_TREE_SESSION:
715 1.1 christos window_tree_draw_session(modedata, sp, &ctx, sx, sy);
716 1.1 christos break;
717 1.1 christos case WINDOW_TREE_WINDOW:
718 1.1 christos window_tree_draw_window(modedata, sp, wlp->window, &ctx, sx, sy);
719 1.1 christos break;
720 1.1 christos case WINDOW_TREE_PANE:
721 1.1 christos screen_write_preview(&ctx, &wp->base, sx, sy);
722 1.1 christos break;
723 1.1 christos }
724 1.1 christos
725 1.1 christos screen_write_stop(&ctx);
726 1.1 christos return (&s);
727 1.1 christos }
728 1.1 christos
729 1.1 christos static int
730 1.1 christos window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
731 1.1 christos {
732 1.1 christos struct window_tree_itemdata *item = itemdata;
733 1.1 christos struct session *s;
734 1.1 christos struct winlink *wl;
735 1.1 christos struct window_pane *wp;
736 1.1 christos const char *cmd;
737 1.1 christos
738 1.1 christos window_tree_pull_item(item, &s, &wl, &wp);
739 1.1 christos
740 1.1 christos switch (item->type) {
741 1.1 christos case WINDOW_TREE_NONE:
742 1.1 christos return (0);
743 1.1 christos case WINDOW_TREE_SESSION:
744 1.1 christos if (s == NULL)
745 1.1 christos return (0);
746 1.1 christos return (strstr(s->name, ss) != NULL);
747 1.1 christos case WINDOW_TREE_WINDOW:
748 1.1 christos if (s == NULL || wl == NULL)
749 1.1 christos return (0);
750 1.1 christos return (strstr(wl->window->name, ss) != NULL);
751 1.1 christos case WINDOW_TREE_PANE:
752 1.1 christos if (s == NULL || wl == NULL || wp == NULL)
753 1.1 christos break;
754 1.1 christos cmd = osdep_get_name(wp->fd, wp->tty);
755 1.1 christos if (cmd == NULL || *cmd == '\0')
756 1.1 christos return (0);
757 1.1 christos return (strstr(cmd, ss) != NULL);
758 1.1 christos }
759 1.1 christos return (0);
760 1.1 christos }
761 1.1 christos
762 1.1 christos static struct screen *
763 1.1 christos window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
764 1.1 christos struct args *args)
765 1.1 christos {
766 1.1 christos struct window_tree_modedata *data;
767 1.1 christos struct screen *s;
768 1.1 christos
769 1.1 christos wp->modedata = data = xcalloc(1, sizeof *data);
770 1.1 christos
771 1.1 christos if (args_has(args, 's'))
772 1.1 christos data->type = WINDOW_TREE_SESSION;
773 1.1 christos else if (args_has(args, 'w'))
774 1.1 christos data->type = WINDOW_TREE_WINDOW;
775 1.1 christos else
776 1.1 christos data->type = WINDOW_TREE_PANE;
777 1.1 christos memcpy(&data->fs, fs, sizeof data->fs);
778 1.1 christos
779 1.1 christos data->wp = wp;
780 1.1 christos data->references = 1;
781 1.1 christos
782 1.1 christos if (args == NULL || !args_has(args, 'F'))
783 1.1 christos data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
784 1.1 christos else
785 1.1 christos data->format = xstrdup(args_get(args, 'F'));
786 1.1 christos if (args == NULL || args->argc == 0)
787 1.1 christos data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
788 1.1 christos else
789 1.1 christos data->command = xstrdup(args->argv[0]);
790 1.1 christos
791 1.1 christos data->data = mode_tree_start(wp, args, window_tree_build,
792 1.1 christos window_tree_draw, window_tree_search, data, window_tree_sort_list,
793 1.1 christos nitems(window_tree_sort_list), &s);
794 1.1 christos
795 1.1 christos mode_tree_build(data->data);
796 1.1 christos mode_tree_draw(data->data);
797 1.1 christos
798 1.1 christos data->type = WINDOW_TREE_NONE;
799 1.1 christos
800 1.1 christos return (s);
801 1.1 christos }
802 1.1 christos
803 1.1 christos static void
804 1.1 christos window_tree_destroy(struct window_tree_modedata *data)
805 1.1 christos {
806 1.1 christos u_int i;
807 1.1 christos
808 1.1 christos if (--data->references != 0)
809 1.1 christos return;
810 1.1 christos
811 1.1 christos mode_tree_free(data->data);
812 1.1 christos
813 1.1 christos for (i = 0; i < data->item_size; i++)
814 1.1 christos window_tree_free_item(data->item_list[i]);
815 1.1 christos free(data->item_list);
816 1.1 christos
817 1.1 christos free(data->format);
818 1.1 christos free(data->command);
819 1.1 christos
820 1.1 christos free(data);
821 1.1 christos }
822 1.1 christos
823 1.1 christos static void
824 1.1 christos window_tree_free(struct window_pane *wp)
825 1.1 christos {
826 1.1 christos struct window_tree_modedata *data = wp->modedata;
827 1.1 christos
828 1.1 christos if (data == NULL)
829 1.1 christos return;
830 1.1 christos
831 1.1 christos data->dead = 1;
832 1.1 christos window_tree_destroy(data);
833 1.1 christos }
834 1.1 christos
835 1.1 christos static void
836 1.1 christos window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
837 1.1 christos {
838 1.1 christos struct window_tree_modedata *data = wp->modedata;
839 1.1 christos
840 1.1 christos mode_tree_resize(data->data, sx, sy);
841 1.1 christos }
842 1.1 christos
843 1.1 christos static char *
844 1.1 christos window_tree_get_target(struct window_tree_itemdata *item,
845 1.1 christos struct cmd_find_state *fs)
846 1.1 christos {
847 1.1 christos struct session *s;
848 1.1 christos struct winlink *wl;
849 1.1 christos struct window_pane *wp;
850 1.1 christos char *target;
851 1.1 christos
852 1.1 christos window_tree_pull_item(item, &s, &wl, &wp);
853 1.1 christos
854 1.1 christos target = NULL;
855 1.1 christos switch (item->type) {
856 1.1 christos case WINDOW_TREE_NONE:
857 1.1 christos break;
858 1.1 christos case WINDOW_TREE_SESSION:
859 1.1 christos if (s == NULL)
860 1.1 christos break;
861 1.1 christos xasprintf(&target, "=%s:", s->name);
862 1.1 christos break;
863 1.1 christos case WINDOW_TREE_WINDOW:
864 1.1 christos if (s == NULL || wl == NULL)
865 1.1 christos break;
866 1.1 christos xasprintf(&target, "=%s:%u.", s->name, wl->idx);
867 1.1 christos break;
868 1.1 christos case WINDOW_TREE_PANE:
869 1.1 christos if (s == NULL || wl == NULL || wp == NULL)
870 1.1 christos break;
871 1.1 christos xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
872 1.1 christos break;
873 1.1 christos }
874 1.1 christos if (target == NULL)
875 1.1 christos cmd_find_clear_state(fs, 0);
876 1.1 christos else
877 1.1 christos cmd_find_from_winlink_pane(fs, wl, wp, 0);
878 1.1 christos return (target);
879 1.1 christos }
880 1.1 christos
881 1.1 christos static void
882 1.1 christos window_tree_command_each(void* modedata, void* itemdata, __unused key_code key)
883 1.1 christos {
884 1.1 christos struct window_tree_modedata *data = modedata;
885 1.1 christos struct window_tree_itemdata *item = itemdata;
886 1.1 christos char *name;
887 1.1 christos struct cmd_find_state fs;
888 1.1 christos
889 1.1 christos name = window_tree_get_target(item, &fs);
890 1.1 christos if (name != NULL)
891 1.1 christos mode_tree_run_command(data->client, &fs, data->entered, name);
892 1.1 christos free(name);
893 1.1 christos }
894 1.1 christos
895 1.1 christos static enum cmd_retval
896 1.1 christos window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
897 1.1 christos {
898 1.1 christos struct window_tree_modedata *data = modedata;
899 1.1 christos
900 1.1 christos if (!data->dead) {
901 1.1 christos mode_tree_build(data->data);
902 1.1 christos mode_tree_draw(data->data);
903 1.1 christos data->wp->flags |= PANE_REDRAW;
904 1.1 christos }
905 1.1 christos window_tree_destroy(data);
906 1.1 christos return (CMD_RETURN_NORMAL);
907 1.1 christos }
908 1.1 christos
909 1.1 christos static int
910 1.1 christos window_tree_command_callback(struct client *c, void *modedata, const char *s,
911 1.1 christos __unused int done)
912 1.1 christos {
913 1.1 christos struct window_tree_modedata *data = modedata;
914 1.1 christos
915 1.1 christos if (data->dead)
916 1.1 christos return (0);
917 1.1 christos
918 1.1 christos data->client = c;
919 1.1 christos data->entered = s;
920 1.1 christos
921 1.1 christos mode_tree_each_tagged(data->data, window_tree_command_each, KEYC_NONE,
922 1.1 christos 1);
923 1.1 christos
924 1.1 christos data->client = NULL;
925 1.1 christos data->entered = NULL;
926 1.1 christos
927 1.1 christos data->references++;
928 1.1 christos cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
929 1.1 christos
930 1.1 christos return (0);
931 1.1 christos }
932 1.1 christos
933 1.1 christos static void
934 1.1 christos window_tree_command_free(void *modedata)
935 1.1 christos {
936 1.1 christos struct window_tree_modedata *data = modedata;
937 1.1 christos
938 1.1 christos window_tree_destroy(data);
939 1.1 christos }
940 1.1 christos
941 1.1 christos static void
942 1.1 christos window_tree_key(struct window_pane *wp, struct client *c,
943 1.1 christos __unused struct session *s, key_code key, struct mouse_event *m)
944 1.1 christos {
945 1.1 christos struct window_tree_modedata *data = wp->modedata;
946 1.1 christos struct window_tree_itemdata *item;
947 1.1 christos char *command, *name, *prompt;
948 1.1 christos struct cmd_find_state fs;
949 1.1 christos int finished;
950 1.1 christos u_int tagged;
951 1.1 christos
952 1.1 christos item = mode_tree_get_current(data->data);
953 1.1 christos finished = mode_tree_key(data->data, c, &key, m);
954 1.1 christos if (item != mode_tree_get_current(data->data))
955 1.1 christos data->offset = 0;
956 1.1 christos switch (key) {
957 1.1 christos case '<':
958 1.1 christos data->offset--;
959 1.1 christos break;
960 1.1 christos case '>':
961 1.1 christos data->offset++;
962 1.1 christos break;
963 1.1 christos case ':':
964 1.1 christos tagged = mode_tree_count_tagged(data->data);
965 1.1 christos if (tagged != 0)
966 1.1 christos xasprintf(&prompt, "(%u tagged) ", tagged);
967 1.1 christos else
968 1.1 christos xasprintf(&prompt, "(current) ");
969 1.1 christos data->references++;
970 1.1 christos status_prompt_set(c, prompt, "", window_tree_command_callback,
971 1.1 christos window_tree_command_free, data, PROMPT_NOFORMAT);
972 1.1 christos free(prompt);
973 1.1 christos break;
974 1.1 christos case '\r':
975 1.1 christos item = mode_tree_get_current(data->data);
976 1.1 christos command = xstrdup(data->command);
977 1.1 christos name = window_tree_get_target(item, &fs);
978 1.1 christos window_pane_reset_mode(wp);
979 1.1 christos if (name != NULL)
980 1.1 christos mode_tree_run_command(c, NULL, command, name);
981 1.1 christos free(name);
982 1.1 christos free(command);
983 1.1 christos return;
984 1.1 christos }
985 1.1 christos if (finished)
986 1.1 christos window_pane_reset_mode(wp);
987 1.1 christos else {
988 1.1 christos mode_tree_draw(data->data);
989 1.1 christos wp->flags |= PANE_REDRAW;
990 1.1 christos }
991 1.1 christos }
992