1 /* $OpenBSD: window-tree.c,v 1.27 2017/11/03 17:02:33 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <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 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" \ 46 "#{?session_grouped, " \ 47 "(group #{session_group}: " \ 48 "#{session_group_list})," \ 49 "}" \ 50 "#{?session_attached, (attached),}" \ 51 "}" \ 52 "}" 53 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; 94 char *format; 95 char *command; 96 int squash_groups; 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; 105 106 int offset; 107 108 int left; 109 int right; 110 u_int start; 111 u_int end; 112 u_int each; 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 243 text = format_single(NULL, data->format, NULL, s, wl, wp); 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 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 269 window_tree_build_window(struct session *s, struct winlink *wl, void* modedata, 270 u_int sort_type, struct mode_tree_item *parent, const char *filter) 271 { 272 struct window_tree_modedata *data = modedata; 273 struct window_tree_itemdata *item; 274 struct mode_tree_item *mti; 275 char *name, *text; 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 286 text = format_single(NULL, data->format, NULL, s, wl, NULL); 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 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; 303 return (1); 304 } 305 306 l = NULL; 307 n = 0; 308 309 TAILQ_FOREACH(wp, &wl->window->panes, entry) { 310 if (!window_tree_filter_pane(s, wl, wp, filter)) 311 continue; 312 l = xreallocarray(l, n + 1, sizeof *l); 313 l[n++] = wp; 314 } 315 if (n == 0) 316 goto empty; 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); 333 334 empty: 335 window_tree_free_item(item); 336 data->item_size--; 337 mode_tree_remove(data->data, mti); 338 return (0); 339 } 340 341 static void 342 window_tree_build_session(struct session *s, void* modedata, 343 u_int sort_type, const char *filter) 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 359 text = format_single(NULL, data->format, NULL, s, NULL, NULL); 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, 389 filter)) 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 401 window_tree_build(void *modedata, u_int sort_type, uint64_t *tag, 402 const char *filter) 403 { 404 struct window_tree_modedata *data = modedata; 405 struct session *s, **l; 406 struct session_group *sg, *current; 407 u_int n, i; 408 409 current = session_group_contains(data->fs.s); 410 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) { 420 if (data->squash_groups && 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 } 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++) 441 window_tree_build_session(l[i], modedata, sort_type, filter); 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: 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; 458 break; 459 } 460 } 461 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 484 static void 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) 487 { 488 struct options *oo = s->options; 489 struct winlink *wl; 490 struct window *w; 491 u_int cx = ctx->s->cx, cy = ctx->s->cy; 492 u_int loop, total, visible, each, width, offset; 493 u_int current, start, end, remaining, i; 494 struct grid_cell gc; 495 int colour, active_colour, left, right; 496 char *label; 497 498 total = winlink_count(&s->windows); 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 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 } 517 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 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 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) { 554 data->left = cx + 2; 555 screen_write_cursormove(ctx, cx + 2, cy); 556 screen_write_vline(ctx, sy, 0, 0); 557 screen_write_cursormove(ctx, cx, cy + sy / 2); 558 screen_write_puts(ctx, &grid_default_cell, "<"); 559 } else 560 data->left = -1; 561 if (right) { 562 data->right = cx + sx - 3; 563 screen_write_cursormove(ctx, cx + sx - 3, cy); 564 screen_write_vline(ctx, sy, 0, 0); 565 screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2); 566 screen_write_puts(ctx, &grid_default_cell, ">"); 567 } else 568 data->right = -1; 569 570 data->start = start; 571 data->end = end; 572 data->each = each; 573 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; 583 584 if (wl == s->curw) 585 gc.fg = active_colour; 586 else 587 gc.fg = colour; 588 589 if (left) 590 offset = 3 + (i * each); 591 else 592 offset = (i * each); 593 if (loop == end - 1) 594 width = each + remaining; 595 else 596 width = each - 1; 597 598 screen_write_cursormove(ctx, cx + offset, cy); 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); 604 window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc, 605 label); 606 free(label); 607 608 if (loop != end - 1) { 609 screen_write_cursormove(ctx, cx + offset + width, cy); 610 screen_write_vline(ctx, sy, 0, 0); 611 } 612 loop++; 613 614 i++; 615 } 616 } 617 618 static void 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) 621 { 622 struct options *oo = s->options; 623 struct window_pane *wp; 624 u_int cx = ctx->s->cx, cy = ctx->s->cy; 625 u_int loop, total, visible, each, width, offset; 626 u_int current, start, end, remaining, i; 627 struct grid_cell gc; 628 int colour, active_colour, left, right, pane_idx; 629 char *label; 630 631 total = window_count_panes(w); 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 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 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 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; 685 686 if (left) { 687 data->left = cx + 2; 688 screen_write_cursormove(ctx, cx + 2, cy); 689 screen_write_vline(ctx, sy, 0, 0); 690 screen_write_cursormove(ctx, cx, cy + sy / 2); 691 screen_write_puts(ctx, &grid_default_cell, "<"); 692 } else 693 data->left = -1; 694 if (right) { 695 data->right = cx + sx - 3; 696 screen_write_cursormove(ctx, cx + sx - 3, cy); 697 screen_write_vline(ctx, sy, 0, 0); 698 screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2); 699 screen_write_puts(ctx, &grid_default_cell, ">"); 700 } else 701 data->right = -1; 702 703 data->start = start; 704 data->end = end; 705 data->each = each; 706 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 } 715 716 if (wp == w->active) 717 gc.fg = active_colour; 718 else 719 gc.fg = colour; 720 721 if (left) 722 offset = 3 + (i * each); 723 else 724 offset = (i * each); 725 if (loop == end - 1) 726 width = each + remaining; 727 else 728 width = each - 1; 729 730 screen_write_cursormove(ctx, cx + offset, cy); 731 screen_write_preview(ctx, &wp->base, width, sy); 732 733 if (window_pane_index(wp, &pane_idx) != 0) 734 pane_idx = loop; 735 xasprintf(&label, " %u ", pane_idx); 736 window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc, 737 label); 738 free(label); 739 740 if (loop != end - 1) { 741 screen_write_cursormove(ctx, cx + offset + width, cy); 742 screen_write_vline(ctx, sy, 0, 0); 743 } 744 loop++; 745 746 i++; 747 } 748 } 749 750 static void 751 window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, 752 u_int sx, u_int sy) 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) 761 return; 762 763 switch (item->type) { 764 case WINDOW_TREE_NONE: 765 break; 766 case WINDOW_TREE_SESSION: 767 window_tree_draw_session(modedata, sp, ctx, sx, sy); 768 break; 769 case WINDOW_TREE_WINDOW: 770 window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy); 771 break; 772 case WINDOW_TREE_PANE: 773 screen_write_preview(ctx, &wp->base, sx, sy); 774 break; 775 } 776 } 777 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 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 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')); 835 if (args == NULL || args->argc == 0) 836 data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); 837 else 838 data->command = xstrdup(args->argv[0]); 839 data->squash_groups = !args_has(args, 'G'); 840 841 data->data = mode_tree_start(wp, args, window_tree_build, 842 window_tree_draw, window_tree_search, data, window_tree_sort_list, 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 867 free(data->format); 868 free(data->command); 869 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 927 cmd_find_from_winlink_pane(fs, wl, wp, 0); 928 return (target); 929 } 930 931 static void 932 window_tree_command_each(void* modedata, void* itemdata, struct client *c, 933 __unused key_code key) 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) 942 mode_tree_run_command(c, &fs, data->entered, name); 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 966 if (s == NULL || data->dead) 967 return (0); 968 969 data->entered = s; 970 mode_tree_each_tagged(data->data, window_tree_command_each, c, 971 KEYC_NONE, 1); 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 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 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; 1054 struct window_tree_itemdata *item, *new_item; 1055 char *name, *prompt; 1056 struct cmd_find_state fs; 1057 int finished; 1058 u_int tagged, x, y; 1059 1060 item = mode_tree_get_current(data->data); 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; 1064 data->offset = 0; 1065 } 1066 if (KEYC_IS_MOUSE(key)) 1067 key = window_tree_mouse(data, key, x, item); 1068 switch (key) { 1069 case '<': 1070 data->offset--; 1071 break; 1072 case '>': 1073 data->offset++; 1074 break; 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) 1090 mode_tree_run_command(c, NULL, data->command, name); 1091 finished = 1; 1092 free(name); 1093 break; 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 } 1102