Annotation of src/usr.bin/tmux/window-tree.c, Revision 1.27
1.27 ! nicm 1: /* $OpenBSD: window-tree.c,v 1.26 2017/11/02 22:00:42 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.16 nicm 299: wp = TAILQ_FIRST(&wl->window->panes);
300: if (TAILQ_NEXT(wp, entry) == NULL) {
301: if (!window_tree_filter_pane(s, wl, wp, filter))
302: goto empty;
1.13 nicm 303: return (1);
1.16 nicm 304: }
1.13 nicm 305:
1.1 nicm 306: l = NULL;
307: n = 0;
1.13 nicm 308:
1.1 nicm 309: TAILQ_FOREACH(wp, &wl->window->panes, entry) {
1.16 nicm 310: if (!window_tree_filter_pane(s, wl, wp, filter))
311: continue;
1.1 nicm 312: l = xreallocarray(l, n + 1, sizeof *l);
313: l[n++] = wp;
314: }
1.16 nicm 315: if (n == 0)
316: goto empty;
1.1 nicm 317:
318: switch (sort_type) {
319: case WINDOW_TREE_BY_INDEX:
320: break;
321: case WINDOW_TREE_BY_NAME:
322: /* Panes don't have names, so leave in number order. */
323: break;
324: case WINDOW_TREE_BY_TIME:
325: qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
326: break;
327: }
328:
329: for (i = 0; i < n; i++)
330: window_tree_build_pane(s, wl, l[i], modedata, mti);
331: free(l);
332: return (1);
1.16 nicm 333:
334: empty:
335: window_tree_free_item(item);
336: data->item_size--;
337: mode_tree_remove(data->data, mti);
338: return (0);
1.1 nicm 339: }
340:
341: static void
342: window_tree_build_session(struct session *s, void* modedata,
1.5 nicm 343: u_int sort_type, const char *filter)
1.1 nicm 344: {
345: struct window_tree_modedata *data = modedata;
346: struct window_tree_itemdata *item;
347: struct mode_tree_item *mti;
348: char *text;
349: struct winlink *wl, **l;
350: u_int n, i, empty;
351: int expanded;
352:
353: item = window_tree_add_item(data);
354: item->type = WINDOW_TREE_SESSION;
355: item->session = s->id;
356: item->winlink = -1;
357: item->pane = -1;
358:
1.15 nicm 359: text = format_single(NULL, data->format, NULL, s, NULL, NULL);
1.1 nicm 360:
361: if (data->type == WINDOW_TREE_SESSION)
362: expanded = 0;
363: else
364: expanded = 1;
365: mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text,
366: expanded);
367: free(text);
368:
369: l = NULL;
370: n = 0;
371: RB_FOREACH(wl, winlinks, &s->windows) {
372: l = xreallocarray(l, n + 1, sizeof *l);
373: l[n++] = wl;
374: }
375: switch (sort_type) {
376: case WINDOW_TREE_BY_INDEX:
377: break;
378: case WINDOW_TREE_BY_NAME:
379: qsort(l, n, sizeof *l, window_tree_cmp_window_name);
380: break;
381: case WINDOW_TREE_BY_TIME:
382: qsort(l, n, sizeof *l, window_tree_cmp_window_time);
383: break;
384: }
385:
386: empty = 0;
387: for (i = 0; i < n; i++) {
388: if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
1.5 nicm 389: filter))
1.1 nicm 390: empty++;
391: }
392: if (empty == n) {
393: window_tree_free_item(item);
394: data->item_size--;
395: mode_tree_remove(data->data, mti);
396: }
397: free(l);
398: }
399:
400: static void
1.5 nicm 401: window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
402: const char *filter)
1.1 nicm 403: {
404: struct window_tree_modedata *data = modedata;
405: struct session *s, **l;
1.24 nicm 406: struct session_group *sg, *current;
1.1 nicm 407: u_int n, i;
408:
1.24 nicm 409: current = session_group_contains(data->fs.s);
410:
1.1 nicm 411: for (i = 0; i < data->item_size; i++)
412: window_tree_free_item(data->item_list[i]);
413: free(data->item_list);
414: data->item_list = NULL;
415: data->item_size = 0;
416:
417: l = NULL;
418: n = 0;
419: RB_FOREACH(s, sessions, &sessions) {
1.23 nicm 420: if (data->squash_groups &&
1.24 nicm 421: (sg = session_group_contains(s)) != NULL) {
422: if ((sg == current && s != data->fs.s) ||
423: (sg != current && s != TAILQ_FIRST(&sg->sessions)))
424: continue;
425: }
1.1 nicm 426: l = xreallocarray(l, n + 1, sizeof *l);
427: l[n++] = s;
428: }
429: switch (sort_type) {
430: case WINDOW_TREE_BY_INDEX:
431: break;
432: case WINDOW_TREE_BY_NAME:
433: qsort(l, n, sizeof *l, window_tree_cmp_session_name);
434: break;
435: case WINDOW_TREE_BY_TIME:
436: qsort(l, n, sizeof *l, window_tree_cmp_session_time);
437: break;
438: }
439:
440: for (i = 0; i < n; i++)
1.5 nicm 441: window_tree_build_session(l[i], modedata, sort_type, filter);
1.1 nicm 442: free(l);
443:
444: switch (data->type) {
445: case WINDOW_TREE_NONE:
446: break;
447: case WINDOW_TREE_SESSION:
448: *tag = (uint64_t)data->fs.s;
449: break;
450: case WINDOW_TREE_WINDOW:
451: *tag = (uint64_t)data->fs.wl;
452: break;
453: case WINDOW_TREE_PANE:
1.18 nicm 454: if (window_count_panes(data->fs.wl->window) == 1)
455: *tag = (uint64_t)data->fs.wl;
456: else
457: *tag = (uint64_t)data->fs.wp;
1.1 nicm 458: break;
459: }
460: }
461:
1.19 nicm 462:
463: static void
464: window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
465: u_int sx, u_int sy, const struct grid_cell *gc, const char *label)
466: {
467: size_t len;
468: u_int ox, oy;
469:
470: len = strlen(label);
471: if (sx == 0 || sy == 1 || len > sx)
472: return;
473: ox = (sx - len + 1) / 2;
474: oy = (sy + 1) / 2;
475:
476: if (ox > 1 && ox + len < sx - 1 && sy >= 3) {
477: screen_write_cursormove(ctx, px + ox - 1, py + oy - 1);
478: screen_write_box(ctx, len + 2, 3);
479: }
480: screen_write_cursormove(ctx, px + ox, py + oy);
481: screen_write_puts(ctx, gc, "%s", label);
482: }
483:
1.6 nicm 484: static void
1.11 nicm 485: window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
486: struct screen_write_ctx *ctx, u_int sx, u_int sy)
1.6 nicm 487: {
488: struct options *oo = s->options;
489: struct winlink *wl;
490: struct window *w;
1.26 nicm 491: u_int cx = ctx->s->cx, cy = ctx->s->cy;
1.9 nicm 492: u_int loop, total, visible, each, width, offset;
493: u_int current, start, end, remaining, i;
1.6 nicm 494: struct grid_cell gc;
1.9 nicm 495: int colour, active_colour, left, right;
1.6 nicm 496: char *label;
497:
1.9 nicm 498: total = winlink_count(&s->windows);
1.6 nicm 499:
500: memcpy(&gc, &grid_default_cell, sizeof gc);
501: colour = options_get_number(oo, "display-panes-colour");
502: active_colour = options_get_number(oo, "display-panes-active-colour");
503:
1.9 nicm 504: if (sx / total < 24) {
505: visible = sx / 24;
506: if (visible == 0)
507: visible = 1;
508: } else
509: visible = total;
510:
511: current = 0;
512: RB_FOREACH(wl, winlinks, &s->windows) {
513: if (wl == s->curw)
514: break;
515: current++;
516: }
1.6 nicm 517:
1.9 nicm 518: if (current < visible) {
519: start = 0;
520: end = visible;
521: } else if (current >= total - visible) {
522: start = total - visible;
523: end = total;
524: } else {
525: start = current - (visible / 2);
526: end = start + visible;
527: }
528:
1.11 nicm 529: if (data->offset < -(int)start)
530: data->offset = -(int)start;
531: if (data->offset > (int)(total - end))
532: data->offset = (int)(total - end);
533: start += data->offset;
534: end += data->offset;
535:
1.9 nicm 536: left = (start != 0);
537: right = (end != total);
538: if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
539: left = right = 0;
540: if (left && right) {
541: each = (sx - 6) / visible;
542: remaining = (sx - 6) - (visible * each);
543: } else if (left || right) {
544: each = (sx - 3) / visible;
545: remaining = (sx - 3) - (visible * each);
546: } else {
547: each = sx / visible;
548: remaining = sx - (visible * each);
549: }
550: if (each == 0)
551: return;
552:
553: if (left) {
1.27 ! nicm 554: data->left = cx + 2;
1.26 nicm 555: screen_write_cursormove(ctx, cx + 2, cy);
1.9 nicm 556: screen_write_vline(ctx, sy, 0, 0);
1.26 nicm 557: screen_write_cursormove(ctx, cx, cy + sy / 2);
1.9 nicm 558: screen_write_puts(ctx, &grid_default_cell, "<");
1.27 ! nicm 559: } else
! 560: data->left = -1;
1.9 nicm 561: if (right) {
1.27 ! nicm 562: data->right = cx + sx - 3;
1.26 nicm 563: screen_write_cursormove(ctx, cx + sx - 3, cy);
1.6 nicm 564: screen_write_vline(ctx, sy, 0, 0);
1.26 nicm 565: screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2);
1.9 nicm 566: screen_write_puts(ctx, &grid_default_cell, ">");
1.27 ! nicm 567: } else
! 568: data->right = -1;
! 569:
! 570: data->start = start;
! 571: data->end = end;
! 572: data->each = each;
1.6 nicm 573:
1.9 nicm 574: i = loop = 0;
575: RB_FOREACH(wl, winlinks, &s->windows) {
576: if (loop == end)
577: break;
578: if (loop < start) {
579: loop++;
580: continue;
581: }
582: w = wl->window;
1.6 nicm 583:
584: if (wl == s->curw)
585: gc.fg = active_colour;
586: else
587: gc.fg = colour;
1.9 nicm 588:
589: if (left)
590: offset = 3 + (i * each);
591: else
592: offset = (i * each);
593: if (loop == end - 1)
1.12 nicm 594: width = each + remaining;
1.6 nicm 595: else
596: width = each - 1;
597:
1.26 nicm 598: screen_write_cursormove(ctx, cx + offset, cy);
1.6 nicm 599: screen_write_preview(ctx, &w->active->base, width, sy);
600:
601: xasprintf(&label, " %u:%s ", wl->idx, w->name);
602: if (strlen(label) > width)
603: xasprintf(&label, " %u ", wl->idx);
1.26 nicm 604: window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc,
605: label);
1.6 nicm 606: free(label);
607:
1.9 nicm 608: if (loop != end - 1) {
1.26 nicm 609: screen_write_cursormove(ctx, cx + offset + width, cy);
1.6 nicm 610: screen_write_vline(ctx, sy, 0, 0);
611: }
1.9 nicm 612: loop++;
613:
614: i++;
1.6 nicm 615: }
616: }
617:
618: static void
1.11 nicm 619: window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
620: struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy)
1.6 nicm 621: {
622: struct options *oo = s->options;
623: struct window_pane *wp;
1.26 nicm 624: u_int cx = ctx->s->cx, cy = ctx->s->cy;
1.9 nicm 625: u_int loop, total, visible, each, width, offset;
626: u_int current, start, end, remaining, i;
1.6 nicm 627: struct grid_cell gc;
1.20 nicm 628: int colour, active_colour, left, right, pane_idx;
1.6 nicm 629: char *label;
630:
1.9 nicm 631: total = window_count_panes(w);
1.6 nicm 632:
633: memcpy(&gc, &grid_default_cell, sizeof gc);
634: colour = options_get_number(oo, "display-panes-colour");
635: active_colour = options_get_number(oo, "display-panes-active-colour");
636:
1.9 nicm 637: if (sx / total < 24) {
638: visible = sx / 24;
639: if (visible == 0)
640: visible = 1;
641: } else
642: visible = total;
643:
644: current = 0;
645: TAILQ_FOREACH(wp, &w->panes, entry) {
646: if (wp == w->active)
647: break;
648: current++;
649: }
650:
651: if (current < visible) {
652: start = 0;
653: end = visible;
654: } else if (current >= total - visible) {
655: start = total - visible;
656: end = total;
657: } else {
658: start = current - (visible / 2);
659: end = start + visible;
660: }
661:
1.11 nicm 662: if (data->offset < -(int)start)
663: data->offset = -(int)start;
664: if (data->offset > (int)(total - end))
665: data->offset = (int)(total - end);
666: start += data->offset;
667: end += data->offset;
668:
1.9 nicm 669: left = (start != 0);
670: right = (end != total);
671: if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
672: left = right = 0;
673: if (left && right) {
674: each = (sx - 6) / visible;
675: remaining = (sx - 6) - (visible * each);
676: } else if (left || right) {
677: each = (sx - 3) / visible;
678: remaining = (sx - 3) - (visible * each);
679: } else {
680: each = sx / visible;
681: remaining = sx - (visible * each);
682: }
683: if (each == 0)
684: return;
1.6 nicm 685:
1.9 nicm 686: if (left) {
1.27 ! nicm 687: data->left = cx + 2;
1.26 nicm 688: screen_write_cursormove(ctx, cx + 2, cy);
1.6 nicm 689: screen_write_vline(ctx, sy, 0, 0);
1.26 nicm 690: screen_write_cursormove(ctx, cx, cy + sy / 2);
1.9 nicm 691: screen_write_puts(ctx, &grid_default_cell, "<");
1.27 ! nicm 692: } else
! 693: data->left = -1;
1.9 nicm 694: if (right) {
1.27 ! nicm 695: data->right = cx + sx - 3;
1.26 nicm 696: screen_write_cursormove(ctx, cx + sx - 3, cy);
1.9 nicm 697: screen_write_vline(ctx, sy, 0, 0);
1.26 nicm 698: screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2);
1.9 nicm 699: screen_write_puts(ctx, &grid_default_cell, ">");
1.27 ! nicm 700: } else
! 701: data->right = -1;
! 702:
! 703: data->start = start;
! 704: data->end = end;
! 705: data->each = each;
1.6 nicm 706:
1.9 nicm 707: i = loop = 0;
708: TAILQ_FOREACH(wp, &w->panes, entry) {
709: if (loop == end)
710: break;
711: if (loop < start) {
712: loop++;
713: continue;
714: }
1.6 nicm 715:
716: if (wp == w->active)
717: gc.fg = active_colour;
718: else
719: gc.fg = colour;
1.9 nicm 720:
721: if (left)
722: offset = 3 + (i * each);
723: else
724: offset = (i * each);
725: if (loop == end - 1)
1.12 nicm 726: width = each + remaining;
1.6 nicm 727: else
728: width = each - 1;
729:
1.26 nicm 730: screen_write_cursormove(ctx, cx + offset, cy);
1.6 nicm 731: screen_write_preview(ctx, &wp->base, width, sy);
732:
1.20 nicm 733: if (window_pane_index(wp, &pane_idx) != 0)
734: pane_idx = loop;
735: xasprintf(&label, " %u ", pane_idx);
1.26 nicm 736: window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc,
737: label);
1.6 nicm 738: free(label);
739:
1.9 nicm 740: if (loop != end - 1) {
1.26 nicm 741: screen_write_cursormove(ctx, cx + offset + width, cy);
1.6 nicm 742: screen_write_vline(ctx, sy, 0, 0);
743: }
1.9 nicm 744: loop++;
745:
746: i++;
1.6 nicm 747: }
748: }
749:
1.26 nicm 750: static void
751: window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx,
752: u_int sx, u_int sy)
1.1 nicm 753: {
754: struct window_tree_itemdata *item = itemdata;
755: struct session *sp;
756: struct winlink *wlp;
757: struct window_pane *wp;
758:
759: window_tree_pull_item(item, &sp, &wlp, &wp);
760: if (wp == NULL)
1.26 nicm 761: return;
1.1 nicm 762:
1.6 nicm 763: switch (item->type) {
764: case WINDOW_TREE_NONE:
1.26 nicm 765: break;
1.6 nicm 766: case WINDOW_TREE_SESSION:
1.26 nicm 767: window_tree_draw_session(modedata, sp, ctx, sx, sy);
1.6 nicm 768: break;
769: case WINDOW_TREE_WINDOW:
1.26 nicm 770: window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy);
1.6 nicm 771: break;
772: case WINDOW_TREE_PANE:
1.26 nicm 773: screen_write_preview(ctx, &wp->base, sx, sy);
1.6 nicm 774: break;
775: }
1.1 nicm 776: }
777:
1.3 nicm 778: static int
779: window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
780: {
781: struct window_tree_itemdata *item = itemdata;
782: struct session *s;
783: struct winlink *wl;
784: struct window_pane *wp;
785: const char *cmd;
786:
787: window_tree_pull_item(item, &s, &wl, &wp);
788:
789: switch (item->type) {
790: case WINDOW_TREE_NONE:
791: return (0);
792: case WINDOW_TREE_SESSION:
793: if (s == NULL)
794: return (0);
795: return (strstr(s->name, ss) != NULL);
796: case WINDOW_TREE_WINDOW:
797: if (s == NULL || wl == NULL)
798: return (0);
799: return (strstr(wl->window->name, ss) != NULL);
800: case WINDOW_TREE_PANE:
801: if (s == NULL || wl == NULL || wp == NULL)
802: break;
803: cmd = get_proc_name(wp->fd, wp->tty);
804: if (cmd == NULL || *cmd == '\0')
805: return (0);
806: return (strstr(cmd, ss) != NULL);
807: }
808: return (0);
809: }
810:
1.1 nicm 811: static struct screen *
812: window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
813: struct args *args)
814: {
815: struct window_tree_modedata *data;
816: struct screen *s;
817:
818: wp->modedata = data = xcalloc(1, sizeof *data);
819:
820: if (args_has(args, 's'))
821: data->type = WINDOW_TREE_SESSION;
822: else if (args_has(args, 'w'))
823: data->type = WINDOW_TREE_WINDOW;
824: else
825: data->type = WINDOW_TREE_PANE;
826: memcpy(&data->fs, fs, sizeof data->fs);
827:
828: data->wp = wp;
829: data->references = 1;
830:
1.15 nicm 831: if (args == NULL || !args_has(args, 'F'))
832: data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
833: else
834: data->format = xstrdup(args_get(args, 'F'));
1.1 nicm 835: if (args == NULL || args->argc == 0)
836: data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
837: else
838: data->command = xstrdup(args->argv[0]);
1.23 nicm 839: data->squash_groups = !args_has(args, 'G');
1.1 nicm 840:
1.4 nicm 841: data->data = mode_tree_start(wp, args, window_tree_build,
842: window_tree_draw, window_tree_search, data, window_tree_sort_list,
1.1 nicm 843: nitems(window_tree_sort_list), &s);
844:
845: mode_tree_build(data->data);
846: mode_tree_draw(data->data);
847:
848: data->type = WINDOW_TREE_NONE;
849:
850: return (s);
851: }
852:
853: static void
854: window_tree_destroy(struct window_tree_modedata *data)
855: {
856: u_int i;
857:
858: if (--data->references != 0)
859: return;
860:
861: mode_tree_free(data->data);
862:
863: for (i = 0; i < data->item_size; i++)
864: window_tree_free_item(data->item_list[i]);
865: free(data->item_list);
866:
1.15 nicm 867: free(data->format);
1.1 nicm 868: free(data->command);
1.15 nicm 869:
1.1 nicm 870: free(data);
871: }
872:
873: static void
874: window_tree_free(struct window_pane *wp)
875: {
876: struct window_tree_modedata *data = wp->modedata;
877:
878: if (data == NULL)
879: return;
880:
881: data->dead = 1;
882: window_tree_destroy(data);
883: }
884:
885: static void
886: window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
887: {
888: struct window_tree_modedata *data = wp->modedata;
889:
890: mode_tree_resize(data->data, sx, sy);
891: }
892:
893: static char *
894: window_tree_get_target(struct window_tree_itemdata *item,
895: struct cmd_find_state *fs)
896: {
897: struct session *s;
898: struct winlink *wl;
899: struct window_pane *wp;
900: char *target;
901:
902: window_tree_pull_item(item, &s, &wl, &wp);
903:
904: target = NULL;
905: switch (item->type) {
906: case WINDOW_TREE_NONE:
907: break;
908: case WINDOW_TREE_SESSION:
909: if (s == NULL)
910: break;
911: xasprintf(&target, "=%s:", s->name);
912: break;
913: case WINDOW_TREE_WINDOW:
914: if (s == NULL || wl == NULL)
915: break;
916: xasprintf(&target, "=%s:%u.", s->name, wl->idx);
917: break;
918: case WINDOW_TREE_PANE:
919: if (s == NULL || wl == NULL || wp == NULL)
920: break;
921: xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
922: break;
923: }
924: if (target == NULL)
925: cmd_find_clear_state(fs, 0);
926: else
1.17 nicm 927: cmd_find_from_winlink_pane(fs, wl, wp, 0);
1.1 nicm 928: return (target);
929: }
930:
931: static void
1.21 nicm 932: window_tree_command_each(void* modedata, void* itemdata, struct client *c,
933: __unused key_code key)
1.1 nicm 934: {
935: struct window_tree_modedata *data = modedata;
936: struct window_tree_itemdata *item = itemdata;
937: char *name;
938: struct cmd_find_state fs;
939:
940: name = window_tree_get_target(item, &fs);
941: if (name != NULL)
1.21 nicm 942: mode_tree_run_command(c, &fs, data->entered, name);
1.1 nicm 943: free(name);
944: }
945:
946: static enum cmd_retval
947: window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
948: {
949: struct window_tree_modedata *data = modedata;
950:
951: if (!data->dead) {
952: mode_tree_build(data->data);
953: mode_tree_draw(data->data);
954: data->wp->flags |= PANE_REDRAW;
955: }
956: window_tree_destroy(data);
957: return (CMD_RETURN_NORMAL);
958: }
959:
960: static int
961: window_tree_command_callback(struct client *c, void *modedata, const char *s,
962: __unused int done)
963: {
964: struct window_tree_modedata *data = modedata;
965:
1.22 nicm 966: if (s == NULL || data->dead)
1.1 nicm 967: return (0);
968:
969: data->entered = s;
1.21 nicm 970: mode_tree_each_tagged(data->data, window_tree_command_each, c,
971: KEYC_NONE, 1);
1.1 nicm 972: data->entered = NULL;
973:
974: data->references++;
975: cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
976:
977: return (0);
978: }
979:
980: static void
981: window_tree_command_free(void *modedata)
982: {
983: struct window_tree_modedata *data = modedata;
984:
985: window_tree_destroy(data);
986: }
987:
1.27 ! nicm 988: static key_code
! 989: window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x,
! 990: struct window_tree_itemdata *item)
! 991: {
! 992: struct session *s;
! 993: struct winlink *wl;
! 994: struct window_pane *wp;
! 995: u_int loop;
! 996:
! 997: if (key != KEYC_MOUSEDOWN1_PANE)
! 998: return (KEYC_NONE);
! 999:
! 1000: if (data->left != -1 && x <= (u_int)data->left)
! 1001: return ('<');
! 1002: if (data->right != -1 && x >= (u_int)data->right)
! 1003: return ('>');
! 1004:
! 1005: if (data->left != -1)
! 1006: x -= data->left;
! 1007: else if (x != 0)
! 1008: x--;
! 1009: if (x == 0 || data->end == 0)
! 1010: x = 0;
! 1011: else {
! 1012: x = x / data->each;
! 1013: if (data->start + x >= data->end)
! 1014: x = data->end - 1;
! 1015: }
! 1016:
! 1017: window_tree_pull_item(item, &s, &wl, &wp);
! 1018: if (item->type == WINDOW_TREE_SESSION) {
! 1019: if (s == NULL)
! 1020: return (KEYC_NONE);
! 1021: mode_tree_expand_current(data->data);
! 1022: loop = 0;
! 1023: RB_FOREACH(wl, winlinks, &s->windows) {
! 1024: if (loop == data->start + x)
! 1025: break;
! 1026: loop++;
! 1027: }
! 1028: if (wl != NULL)
! 1029: mode_tree_set_current(data->data, (uint64_t)wl);
! 1030: return ('\r');
! 1031: }
! 1032: if (item->type == WINDOW_TREE_WINDOW) {
! 1033: if (wl == NULL)
! 1034: return (KEYC_NONE);
! 1035: mode_tree_expand_current(data->data);
! 1036: loop = 0;
! 1037: TAILQ_FOREACH(wp, &wl->window->panes, entry) {
! 1038: if (loop == data->start + x)
! 1039: break;
! 1040: loop++;
! 1041: }
! 1042: if (wp != NULL)
! 1043: mode_tree_set_current(data->data, (uint64_t)wp);
! 1044: return ('\r');
! 1045: }
! 1046: return (KEYC_NONE);
! 1047: }
! 1048:
1.1 nicm 1049: static void
1050: window_tree_key(struct window_pane *wp, struct client *c,
1051: __unused struct session *s, key_code key, struct mouse_event *m)
1052: {
1053: struct window_tree_modedata *data = wp->modedata;
1.27 ! nicm 1054: struct window_tree_itemdata *item, *new_item;
1.21 nicm 1055: char *name, *prompt;
1.1 nicm 1056: struct cmd_find_state fs;
1057: int finished;
1.27 ! nicm 1058: u_int tagged, x, y;
1.1 nicm 1059:
1.11 nicm 1060: item = mode_tree_get_current(data->data);
1.27 ! nicm 1061: finished = mode_tree_key(data->data, c, &key, m, &x, &y);
! 1062: if (item != (new_item = mode_tree_get_current(data->data))) {
! 1063: item = new_item;
1.11 nicm 1064: data->offset = 0;
1.27 ! nicm 1065: }
! 1066: if (KEYC_IS_MOUSE(key))
! 1067: key = window_tree_mouse(data, key, x, item);
1.1 nicm 1068: switch (key) {
1.11 nicm 1069: case '<':
1070: data->offset--;
1071: break;
1072: case '>':
1073: data->offset++;
1074: break;
1.1 nicm 1075: case ':':
1076: tagged = mode_tree_count_tagged(data->data);
1077: if (tagged != 0)
1078: xasprintf(&prompt, "(%u tagged) ", tagged);
1079: else
1080: xasprintf(&prompt, "(current) ");
1081: data->references++;
1082: status_prompt_set(c, prompt, "", window_tree_command_callback,
1083: window_tree_command_free, data, PROMPT_NOFORMAT);
1084: free(prompt);
1085: break;
1086: case '\r':
1087: item = mode_tree_get_current(data->data);
1088: name = window_tree_get_target(item, &fs);
1089: if (name != NULL)
1.21 nicm 1090: mode_tree_run_command(c, NULL, data->command, name);
1091: finished = 1;
1.1 nicm 1092: free(name);
1.21 nicm 1093: break;
1.1 nicm 1094: }
1095: if (finished)
1096: window_pane_reset_mode(wp);
1097: else {
1098: mode_tree_draw(data->data);
1099: wp->flags |= PANE_REDRAW;
1100: }
1101: }