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