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