1 /* $OpenBSD: mode-tree.c,v 1.9 2017/08/23 09:39:11 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 <ctype.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 26 #include "tmux.h" 27 28 struct mode_tree_item; 29 TAILQ_HEAD(mode_tree_list, mode_tree_item); 30 31 struct mode_tree_data { 32 int dead; 33 u_int references; 34 35 struct window_pane *wp; 36 void *modedata; 37 38 const char **sort_list; 39 u_int sort_size; 40 u_int sort_type; 41 42 void (*buildcb)(void *, u_int, uint64_t *, 43 const char *); 44 struct screen *(*drawcb)(void *, void *, u_int, u_int); 45 int (*searchcb)(void*, void *, const char *); 46 47 struct mode_tree_list children; 48 struct mode_tree_list saved; 49 50 struct mode_tree_line *line_list; 51 u_int line_size; 52 53 u_int depth; 54 55 u_int width; 56 u_int height; 57 58 u_int offset; 59 u_int current; 60 61 struct screen screen; 62 63 int preview; 64 char *search; 65 char *filter; 66 }; 67 68 struct mode_tree_item { 69 struct mode_tree_item *parent; 70 void *itemdata; 71 u_int line; 72 73 uint64_t tag; 74 const char *name; 75 const char *text; 76 77 int expanded; 78 int tagged; 79 80 struct mode_tree_list children; 81 TAILQ_ENTRY(mode_tree_item) entry; 82 }; 83 84 struct mode_tree_line { 85 struct mode_tree_item *item; 86 u_int depth; 87 int last; 88 int flat; 89 }; 90 91 static void mode_tree_free_items(struct mode_tree_list *); 92 93 static struct mode_tree_item * 94 mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag) 95 { 96 struct mode_tree_item *mti, *child; 97 98 TAILQ_FOREACH(mti, mtl, entry) { 99 if (mti->tag == tag) 100 return (mti); 101 child = mode_tree_find_item(&mti->children, tag); 102 if (child != NULL) 103 return (child); 104 } 105 return (NULL); 106 } 107 108 static void 109 mode_tree_free_item(struct mode_tree_item *mti) 110 { 111 mode_tree_free_items(&mti->children); 112 113 free((void *)mti->name); 114 free((void *)mti->text); 115 116 free(mti); 117 } 118 119 static void 120 mode_tree_free_items(struct mode_tree_list *mtl) 121 { 122 struct mode_tree_item *mti, *mti1; 123 124 TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) { 125 TAILQ_REMOVE(mtl, mti, entry); 126 mode_tree_free_item(mti); 127 } 128 } 129 130 static void 131 mode_tree_clear_lines(struct mode_tree_data *mtd) 132 { 133 free(mtd->line_list); 134 mtd->line_list = NULL; 135 mtd->line_size = 0; 136 } 137 138 static void 139 mode_tree_build_lines(struct mode_tree_data *mtd, 140 struct mode_tree_list *mtl, u_int depth) 141 { 142 struct mode_tree_item *mti; 143 struct mode_tree_line *line; 144 u_int i; 145 int flat = 1; 146 147 mtd->depth = depth; 148 TAILQ_FOREACH(mti, mtl, entry) { 149 mtd->line_list = xreallocarray(mtd->line_list, 150 mtd->line_size + 1, sizeof *mtd->line_list); 151 152 line = &mtd->line_list[mtd->line_size++]; 153 line->item = mti; 154 line->depth = depth; 155 line->last = (mti == TAILQ_LAST(mtl, mode_tree_list)); 156 157 mti->line = (mtd->line_size - 1); 158 if (!TAILQ_EMPTY(&mti->children)) 159 flat = 0; 160 if (mti->expanded) 161 mode_tree_build_lines(mtd, &mti->children, depth + 1); 162 } 163 TAILQ_FOREACH(mti, mtl, entry) { 164 for (i = 0; i < mtd->line_size; i++) { 165 line = &mtd->line_list[i]; 166 if (line->item == mti) 167 line->flat = flat; 168 } 169 } 170 } 171 172 static void 173 mode_tree_clear_tagged(struct mode_tree_list *mtl) 174 { 175 struct mode_tree_item *mti; 176 177 TAILQ_FOREACH(mti, mtl, entry) { 178 mti->tagged = 0; 179 mode_tree_clear_tagged(&mti->children); 180 } 181 } 182 183 static void 184 mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag) 185 { 186 u_int i; 187 188 for (i = 0; i < mtd->line_size; i++) { 189 if (mtd->line_list[i].item->tag == tag) 190 break; 191 } 192 if (i != mtd->line_size) { 193 mtd->current = i; 194 if (mtd->current > mtd->height - 1) 195 mtd->offset = 1 + mtd->current - mtd->height; 196 else 197 mtd->offset = 0; 198 } else { 199 mtd->current = 0; 200 mtd->offset = 0; 201 } 202 } 203 204 void 205 mode_tree_up(struct mode_tree_data *mtd, int wrap) 206 { 207 if (mtd->current == 0) { 208 if (wrap) { 209 mtd->current = mtd->line_size - 1; 210 if (mtd->line_size >= mtd->height) 211 mtd->offset = mtd->line_size - mtd->height; 212 } 213 } else { 214 mtd->current--; 215 if (mtd->current < mtd->offset) 216 mtd->offset--; 217 } 218 } 219 220 void 221 mode_tree_down(struct mode_tree_data *mtd, int wrap) 222 { 223 if (mtd->current == mtd->line_size - 1) { 224 if (wrap) { 225 mtd->current = 0; 226 mtd->offset = 0; 227 } 228 } else { 229 mtd->current++; 230 if (mtd->current > mtd->offset + mtd->height - 1) 231 mtd->offset++; 232 } 233 } 234 235 void * 236 mode_tree_get_current(struct mode_tree_data *mtd) 237 { 238 return (mtd->line_list[mtd->current].item->itemdata); 239 } 240 241 u_int 242 mode_tree_count_tagged(struct mode_tree_data *mtd) 243 { 244 struct mode_tree_item *mti; 245 u_int i, tagged; 246 247 tagged = 0; 248 for (i = 0; i < mtd->line_size; i++) { 249 mti = mtd->line_list[i].item; 250 if (mti->tagged) 251 tagged++; 252 } 253 return (tagged); 254 } 255 256 void 257 mode_tree_each_tagged(struct mode_tree_data *mtd, void (*cb)(void *, void *, 258 key_code), key_code key, int current) 259 { 260 struct mode_tree_item *mti; 261 u_int i; 262 int fired; 263 264 fired = 0; 265 for (i = 0; i < mtd->line_size; i++) { 266 mti = mtd->line_list[i].item; 267 if (mti->tagged) { 268 fired = 1; 269 cb(mtd->modedata, mti->itemdata, key); 270 } 271 } 272 if (!fired && current) { 273 mti = mtd->line_list[mtd->current].item; 274 cb(mtd->modedata, mti->itemdata, key); 275 } 276 } 277 278 struct mode_tree_data * 279 mode_tree_start(struct window_pane *wp, struct args *args, 280 void (*buildcb)(void *, u_int, uint64_t *, const char *), 281 struct screen *(*drawcb)(void *, void *, u_int, u_int), 282 int (*searchcb)(void *, void *, const char *), void *modedata, 283 const char **sort_list, u_int sort_size, struct screen **s) 284 { 285 struct mode_tree_data *mtd; 286 const char *sort; 287 u_int i; 288 289 mtd = xcalloc(1, sizeof *mtd); 290 mtd->references = 1; 291 292 mtd->wp = wp; 293 mtd->modedata = modedata; 294 295 mtd->sort_list = sort_list; 296 mtd->sort_size = sort_size; 297 mtd->sort_type = 0; 298 299 mtd->preview = !args_has(args, 'N'); 300 301 sort = args_get(args, 'O'); 302 if (sort != NULL) { 303 for (i = 0; i < sort_size; i++) { 304 if (strcasecmp(sort, sort_list[i]) == 0) 305 mtd->sort_type = i; 306 } 307 } 308 309 if (args_has(args, 'f')) 310 mtd->filter = xstrdup(args_get(args, 'f')); 311 else 312 mtd->filter = NULL; 313 314 mtd->buildcb = buildcb; 315 mtd->drawcb = drawcb; 316 mtd->searchcb = searchcb; 317 318 TAILQ_INIT(&mtd->children); 319 320 *s = &mtd->screen; 321 screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); 322 (*s)->mode &= ~MODE_CURSOR; 323 324 return (mtd); 325 } 326 327 void 328 mode_tree_build(struct mode_tree_data *mtd) 329 { 330 struct screen *s = &mtd->screen; 331 uint64_t tag; 332 333 if (mtd->line_list != NULL) 334 tag = mtd->line_list[mtd->current].item->tag; 335 else 336 tag = 0; 337 338 TAILQ_CONCAT(&mtd->saved, &mtd->children, entry); 339 TAILQ_INIT(&mtd->children); 340 341 mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter); 342 if (TAILQ_EMPTY(&mtd->children)) 343 mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL); 344 345 mode_tree_free_items(&mtd->saved); 346 TAILQ_INIT(&mtd->saved); 347 348 mode_tree_clear_lines(mtd); 349 mode_tree_build_lines(mtd, &mtd->children, 0); 350 351 mode_tree_set_current(mtd, tag); 352 353 mtd->width = screen_size_x(s); 354 if (mtd->preview) { 355 mtd->height = (screen_size_y(s) / 3) * 2; 356 if (mtd->height > mtd->line_size) 357 mtd->height = screen_size_y(s) / 2; 358 if (mtd->height < 10) 359 mtd->height = screen_size_y(s); 360 if (screen_size_y(s) - mtd->height < 2) 361 mtd->height = screen_size_y(s); 362 } else 363 mtd->height = screen_size_y(s); 364 } 365 366 static void 367 mode_tree_remove_ref(struct mode_tree_data *mtd) 368 { 369 if (--mtd->references == 0) 370 free(mtd); 371 } 372 373 void 374 mode_tree_free(struct mode_tree_data *mtd) 375 { 376 mode_tree_free_items(&mtd->children); 377 mode_tree_clear_lines(mtd); 378 screen_free(&mtd->screen); 379 380 free(mtd->search); 381 free(mtd->filter); 382 383 mtd->dead = 1; 384 mode_tree_remove_ref(mtd); 385 } 386 387 void 388 mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy) 389 { 390 struct screen *s = &mtd->screen; 391 392 screen_resize(s, sx, sy, 0); 393 394 mode_tree_build(mtd); 395 mode_tree_draw(mtd); 396 397 mtd->wp->flags |= PANE_REDRAW; 398 } 399 400 struct mode_tree_item * 401 mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent, 402 void *itemdata, uint64_t tag, const char *name, const char *text, 403 int expanded) 404 { 405 struct mode_tree_item *mti, *saved; 406 407 log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag, 408 name, text); 409 410 mti = xcalloc(1, sizeof *mti); 411 mti->parent = parent; 412 mti->itemdata = itemdata; 413 414 mti->tag = tag; 415 mti->name = xstrdup(name); 416 mti->text = xstrdup(text); 417 418 saved = mode_tree_find_item(&mtd->saved, tag); 419 if (saved != NULL) { 420 if (parent == NULL || (parent != NULL && parent->expanded)) 421 mti->tagged = saved->tagged; 422 mti->expanded = saved->expanded; 423 } else if (expanded == -1) 424 mti->expanded = 1; 425 else 426 mti->expanded = expanded; 427 428 TAILQ_INIT(&mti->children); 429 430 if (parent != NULL) 431 TAILQ_INSERT_TAIL(&parent->children, mti, entry); 432 else 433 TAILQ_INSERT_TAIL(&mtd->children, mti, entry); 434 435 return (mti); 436 } 437 438 void 439 mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti) 440 { 441 struct mode_tree_item *parent = mti->parent; 442 443 if (parent != NULL) 444 TAILQ_REMOVE(&parent->children, mti, entry); 445 else 446 TAILQ_REMOVE(&mtd->children, mti, entry); 447 mode_tree_free_item(mti); 448 } 449 450 void 451 mode_tree_draw(struct mode_tree_data *mtd) 452 { 453 struct window_pane *wp = mtd->wp; 454 struct screen *s = &mtd->screen, *box = NULL; 455 struct mode_tree_line *line; 456 struct mode_tree_item *mti; 457 struct options *oo = wp->window->options; 458 struct screen_write_ctx ctx; 459 struct grid_cell gc0, gc; 460 u_int w, h, i, j, sy, box_x, box_y; 461 char *text, *start, key[7]; 462 const char *tag, *symbol; 463 size_t size; 464 int keylen; 465 466 if (mtd->line_size == 0) 467 return; 468 469 memcpy(&gc0, &grid_default_cell, sizeof gc0); 470 memcpy(&gc, &grid_default_cell, sizeof gc); 471 style_apply(&gc, oo, "mode-style"); 472 473 w = mtd->width; 474 h = mtd->height; 475 476 screen_write_start(&ctx, NULL, s); 477 screen_write_clearscreen(&ctx, 8); 478 479 if (mtd->line_size > 10) 480 keylen = 6; 481 else 482 keylen = 4; 483 484 for (i = 0; i < mtd->line_size; i++) { 485 if (i < mtd->offset) 486 continue; 487 if (i > mtd->offset + h - 1) 488 break; 489 490 line = &mtd->line_list[i]; 491 mti = line->item; 492 493 screen_write_cursormove(&ctx, 0, i - mtd->offset); 494 495 if (i < 10) 496 snprintf(key, sizeof key, "(%c) ", '0' + i); 497 else if (i < 36) 498 snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10)); 499 else 500 *key = '\0'; 501 502 if (line->flat) 503 symbol = ""; 504 else if (TAILQ_EMPTY(&mti->children)) 505 symbol = " "; 506 else if (mti->expanded) 507 symbol = "- "; 508 else 509 symbol = "+ "; 510 511 if (line->depth == 0) 512 start = xstrdup(symbol); 513 else { 514 size = (4 * line->depth) + 32; 515 516 start = xcalloc(1, size); 517 for (j = 1; j < line->depth; j++) { 518 if (mti->parent != NULL && 519 mtd->line_list[mti->parent->line].last) 520 strlcat(start, " ", size); 521 else 522 strlcat(start, "\001x\001 ", size); 523 } 524 if (line->last) 525 strlcat(start, "\001mq\001> ", size); 526 else 527 strlcat(start, "\001tq\001> ", size); 528 strlcat(start, symbol, size); 529 } 530 531 if (mti->tagged) 532 tag = "*"; 533 else 534 tag = ""; 535 xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start, 536 mti->name, tag, mti->text); 537 free(start); 538 539 if (mti->tagged) { 540 gc.attr ^= GRID_ATTR_BRIGHT; 541 gc0.attr ^= GRID_ATTR_BRIGHT; 542 } 543 544 if (i != mtd->current) { 545 screen_write_puts(&ctx, &gc0, "%.*s", w, text); 546 screen_write_clearendofline(&ctx, 8); 547 } else 548 screen_write_puts(&ctx, &gc, "%-*.*s", w, w, text); 549 free(text); 550 551 if (mti->tagged) { 552 gc.attr ^= GRID_ATTR_BRIGHT; 553 gc0.attr ^= GRID_ATTR_BRIGHT; 554 } 555 } 556 557 sy = screen_size_y(s); 558 if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) { 559 screen_write_stop(&ctx); 560 return; 561 } 562 563 line = &mtd->line_list[mtd->current]; 564 mti = line->item; 565 566 screen_write_cursormove(&ctx, 0, h); 567 screen_write_box(&ctx, w, sy - h); 568 569 xasprintf(&text, " %s (sort: %s) ", mti->name, 570 mtd->sort_list[mtd->sort_type]); 571 if (w - 2 >= strlen(text)) { 572 screen_write_cursormove(&ctx, 1, h); 573 screen_write_puts(&ctx, &gc0, "%s", text); 574 } 575 free(text); 576 577 box_x = w - 4; 578 box_y = sy - h - 2; 579 580 if (box_x != 0 && box_y != 0) 581 box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y); 582 if (box != NULL) { 583 screen_write_cursormove(&ctx, 2, h + 1); 584 screen_write_copy(&ctx, box, 0, 0, box_x, box_y, NULL, NULL); 585 586 screen_free(box); 587 } 588 589 screen_write_stop(&ctx); 590 } 591 592 static struct mode_tree_item * 593 mode_tree_search_for(struct mode_tree_data *mtd) 594 { 595 struct mode_tree_item *mti, *last, *next; 596 597 if (mtd->search == NULL) 598 return (NULL); 599 600 mti = last = mtd->line_list[mtd->current].item; 601 for (;;) { 602 if (!TAILQ_EMPTY(&mti->children)) 603 mti = TAILQ_FIRST(&mti->children); 604 else if ((next = TAILQ_NEXT(mti, entry)) != NULL) 605 mti = next; 606 else { 607 for (;;) { 608 mti = mti->parent; 609 if (mti == NULL) 610 break; 611 if ((next = TAILQ_NEXT(mti, entry)) != NULL) { 612 mti = next; 613 break; 614 } 615 } 616 } 617 if (mti == NULL) 618 mti = TAILQ_FIRST(&mtd->children); 619 if (mti == last) 620 break; 621 622 if (mtd->searchcb == NULL) { 623 if (strstr(mti->name, mtd->search) != NULL) 624 return (mti); 625 continue; 626 } 627 if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search)) 628 return (mti); 629 } 630 return (NULL); 631 } 632 633 static void 634 mode_tree_search_set(struct mode_tree_data *mtd) 635 { 636 struct mode_tree_item *mti, *loop; 637 uint64_t tag; 638 639 mti = mode_tree_search_for(mtd); 640 if (mti == NULL) 641 return; 642 tag = mti->tag; 643 644 loop = mti->parent; 645 while (loop != NULL) { 646 loop->expanded = 1; 647 loop = loop->parent; 648 } 649 650 mode_tree_build(mtd); 651 mode_tree_set_current(mtd, tag); 652 mode_tree_draw(mtd); 653 mtd->wp->flags |= PANE_REDRAW; 654 } 655 656 static int 657 mode_tree_search_callback(__unused struct client *c, void *data, const char *s, 658 __unused int done) 659 { 660 struct mode_tree_data *mtd = data; 661 662 if (mtd->dead) 663 return (0); 664 665 free(mtd->search); 666 if (s == NULL || *s == '\0') { 667 mtd->search = NULL; 668 return (0); 669 } 670 mtd->search = xstrdup(s); 671 mode_tree_search_set(mtd); 672 673 return (0); 674 } 675 676 static void 677 mode_tree_search_free(void *data) 678 { 679 mode_tree_remove_ref(data); 680 } 681 682 static int 683 mode_tree_filter_callback(__unused struct client *c, void *data, const char *s, 684 __unused int done) 685 { 686 struct mode_tree_data *mtd = data; 687 688 if (mtd->dead) 689 return (0); 690 691 if (mtd->filter != NULL) 692 free(mtd->filter); 693 if (s == NULL || *s == '\0') 694 mtd->filter = NULL; 695 else 696 mtd->filter = xstrdup(s); 697 698 mode_tree_build(mtd); 699 mode_tree_draw(mtd); 700 mtd->wp->flags |= PANE_REDRAW; 701 702 return (0); 703 } 704 705 static void 706 mode_tree_filter_free(void *data) 707 { 708 mode_tree_remove_ref(data); 709 } 710 711 int 712 mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, 713 struct mouse_event *m) 714 { 715 struct mode_tree_line *line; 716 struct mode_tree_item *current, *parent; 717 u_int i, x, y; 718 int choice; 719 key_code tmp; 720 721 if (*key == KEYC_MOUSEDOWN1_PANE) { 722 if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) { 723 *key = KEYC_NONE; 724 return (0); 725 } 726 if (x > mtd->width || y > mtd->height) { 727 *key = KEYC_NONE; 728 return (0); 729 } 730 if (mtd->offset + y < mtd->line_size) { 731 mtd->current = mtd->offset + y; 732 *key = '\r'; 733 return (0); 734 } 735 } 736 737 line = &mtd->line_list[mtd->current]; 738 current = line->item; 739 740 choice = -1; 741 if (*key >= '0' && *key <= '9') 742 choice = (*key) - '0'; 743 else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) { 744 tmp = (*key) & KEYC_MASK_KEY; 745 if (tmp >= 'a' && tmp <= 'z') 746 choice = 10 + (tmp - 'a'); 747 } 748 if (choice != -1) { 749 if ((u_int)choice > mtd->line_size - 1) { 750 *key = KEYC_NONE; 751 return (0); 752 } 753 mtd->current = choice; 754 *key = '\r'; 755 return (0); 756 } 757 758 switch (*key) { 759 case 'q': 760 case '\033': /* Escape */ 761 return (1); 762 case KEYC_UP: 763 case 'k': 764 case KEYC_WHEELUP_PANE: 765 mode_tree_up(mtd, 1); 766 break; 767 case KEYC_DOWN: 768 case 'j': 769 case KEYC_WHEELDOWN_PANE: 770 mode_tree_down(mtd, 1); 771 break; 772 case KEYC_PPAGE: 773 case '\002': /* C-b */ 774 for (i = 0; i < mtd->height; i++) { 775 if (mtd->current == 0) 776 break; 777 mode_tree_up(mtd, 1); 778 } 779 break; 780 case KEYC_NPAGE: 781 case '\006': /* C-f */ 782 for (i = 0; i < mtd->height; i++) { 783 if (mtd->current == mtd->line_size - 1) 784 break; 785 mode_tree_down(mtd, 1); 786 } 787 break; 788 case KEYC_HOME: 789 mtd->current = 0; 790 mtd->offset = 0; 791 break; 792 case KEYC_END: 793 mtd->current = mtd->line_size - 1; 794 if (mtd->current > mtd->height - 1) 795 mtd->offset = mtd->current - mtd->height; 796 else 797 mtd->offset = 0; 798 break; 799 case 't': 800 /* 801 * Do not allow parents and children to both be tagged: untag 802 * all parents and children of current. 803 */ 804 if (!current->tagged) { 805 parent = current->parent; 806 while (parent != NULL) { 807 parent->tagged = 0; 808 parent = parent->parent; 809 } 810 mode_tree_clear_tagged(¤t->children); 811 current->tagged = 1; 812 } else 813 current->tagged = 0; 814 mode_tree_down(mtd, 0); 815 break; 816 case 'T': 817 for (i = 0; i < mtd->line_size; i++) 818 mtd->line_list[i].item->tagged = 0; 819 break; 820 case '\024': /* C-t */ 821 for (i = 0; i < mtd->line_size; i++) { 822 if (mtd->line_list[i].item->parent == NULL) 823 mtd->line_list[i].item->tagged = 1; 824 else 825 mtd->line_list[i].item->tagged = 0; 826 } 827 break; 828 case 'O': 829 mtd->sort_type++; 830 if (mtd->sort_type == mtd->sort_size) 831 mtd->sort_type = 0; 832 mode_tree_build(mtd); 833 break; 834 case KEYC_LEFT: 835 case '-': 836 if (line->flat || !current->expanded) 837 current = current->parent; 838 if (current == NULL) 839 mode_tree_up(mtd, 0); 840 else { 841 current->expanded = 0; 842 mtd->current = current->line; 843 mode_tree_build(mtd); 844 } 845 break; 846 case KEYC_RIGHT: 847 case '+': 848 if (line->flat || current->expanded) 849 mode_tree_down(mtd, 0); 850 else if (!line->flat) { 851 current->expanded = 1; 852 mode_tree_build(mtd); 853 } 854 break; 855 case '\023': /* C-s */ 856 mtd->references++; 857 status_prompt_set(c, "(search) ", "", 858 mode_tree_search_callback, mode_tree_search_free, mtd, 859 PROMPT_NOFORMAT); 860 break; 861 case 'n': 862 mode_tree_search_set(mtd); 863 break; 864 case 'f': 865 mtd->references++; 866 status_prompt_set(c, "(filter) ", mtd->filter, 867 mode_tree_filter_callback, mode_tree_filter_free, mtd, 868 PROMPT_NOFORMAT); 869 break; 870 case 'v': 871 mtd->preview = !mtd->preview; 872 mode_tree_build(mtd); 873 break; 874 } 875 return (0); 876 } 877 878 void 879 mode_tree_run_command(struct client *c, struct cmd_find_state *fs, 880 const char *template, const char *name) 881 { 882 struct cmdq_item *new_item; 883 struct cmd_list *cmdlist; 884 char *command, *cause; 885 886 command = cmd_template_replace(template, name, 1); 887 if (command == NULL || *command == '\0') { 888 free(command); 889 return; 890 } 891 892 cmdlist = cmd_string_parse(command, NULL, 0, &cause); 893 if (cmdlist == NULL) { 894 if (cause != NULL && c != NULL) { 895 *cause = toupper((u_char)*cause); 896 status_message_set(c, "%s", cause); 897 } 898 free(cause); 899 } else { 900 new_item = cmdq_get_command(cmdlist, fs, NULL, 0); 901 cmdq_append(c, new_item); 902 cmd_list_free(cmdlist); 903 } 904 905 free(command); 906 } 907