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