1*be3910bdSnicm /* $OpenBSD: mode-tree.c,v 1.72 2024/12/16 08:54:34 nicm Exp $ */ 2a42faf7dSnicm 3a42faf7dSnicm /* 4a42faf7dSnicm * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com> 5a42faf7dSnicm * 6a42faf7dSnicm * Permission to use, copy, modify, and distribute this software for any 7a42faf7dSnicm * purpose with or without fee is hereby granted, provided that the above 8a42faf7dSnicm * copyright notice and this permission notice appear in all copies. 9a42faf7dSnicm * 10a42faf7dSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11a42faf7dSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12a42faf7dSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13a42faf7dSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14a42faf7dSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15a42faf7dSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16a42faf7dSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17a42faf7dSnicm */ 18a42faf7dSnicm 19a42faf7dSnicm #include <sys/types.h> 20a42faf7dSnicm 21a42faf7dSnicm #include <ctype.h> 22a42faf7dSnicm #include <stdio.h> 23a42faf7dSnicm #include <stdlib.h> 24a42faf7dSnicm #include <string.h> 25a42faf7dSnicm 26a42faf7dSnicm #include "tmux.h" 27a42faf7dSnicm 28cea02a81Snicm enum mode_tree_search_dir { 29cea02a81Snicm MODE_TREE_SEARCH_FORWARD, 30cea02a81Snicm MODE_TREE_SEARCH_BACKWARD 31cea02a81Snicm }; 32cea02a81Snicm 3375c5b1bcSnicm enum mode_tree_preview { 3475c5b1bcSnicm MODE_TREE_PREVIEW_OFF, 3575c5b1bcSnicm MODE_TREE_PREVIEW_NORMAL, 3675c5b1bcSnicm MODE_TREE_PREVIEW_BIG 3775c5b1bcSnicm }; 3875c5b1bcSnicm 39a42faf7dSnicm struct mode_tree_item; 40a42faf7dSnicm TAILQ_HEAD(mode_tree_list, mode_tree_item); 41a42faf7dSnicm 42a42faf7dSnicm struct mode_tree_data { 43943a08b1Snicm int dead; 44943a08b1Snicm u_int references; 454f5e4c93Snicm int zoomed; 46943a08b1Snicm 47a42faf7dSnicm struct window_pane *wp; 48a42faf7dSnicm void *modedata; 491335341aSnicm const struct menu_item *menu; 50a42faf7dSnicm 51a42faf7dSnicm const char **sort_list; 52a42faf7dSnicm u_int sort_size; 533f6f9f7dSnicm struct mode_tree_sort_criteria sort_crit; 54a42faf7dSnicm 552b7e51f7Snicm mode_tree_build_cb buildcb; 562b7e51f7Snicm mode_tree_draw_cb drawcb; 572b7e51f7Snicm mode_tree_search_cb searchcb; 58f43bc87cSnicm mode_tree_menu_cb menucb; 5967c16a7cSnicm mode_tree_height_cb heightcb; 60438eed14Snicm mode_tree_key_cb keycb; 61a42faf7dSnicm 62a42faf7dSnicm struct mode_tree_list children; 63a42faf7dSnicm struct mode_tree_list saved; 64a42faf7dSnicm 65a42faf7dSnicm struct mode_tree_line *line_list; 66a42faf7dSnicm u_int line_size; 67a42faf7dSnicm 68a42faf7dSnicm u_int depth; 69a42faf7dSnicm 70a42faf7dSnicm u_int width; 71a42faf7dSnicm u_int height; 72a42faf7dSnicm 73a42faf7dSnicm u_int offset; 74a42faf7dSnicm u_int current; 75a42faf7dSnicm 76a42faf7dSnicm struct screen screen; 77943a08b1Snicm 78f84988b9Snicm int preview; 79024c311aSnicm char *search; 80024c311aSnicm char *filter; 814d1af203Snicm int no_matches; 82cea02a81Snicm enum mode_tree_search_dir search_dir; 83a42faf7dSnicm }; 84a42faf7dSnicm 85a42faf7dSnicm struct mode_tree_item { 86a42faf7dSnicm struct mode_tree_item *parent; 87a42faf7dSnicm void *itemdata; 88a42faf7dSnicm u_int line; 89a42faf7dSnicm 90438eed14Snicm key_code key; 91438eed14Snicm const char *keystr; 92438eed14Snicm size_t keylen; 93438eed14Snicm 94a42faf7dSnicm uint64_t tag; 95a42faf7dSnicm const char *name; 96a42faf7dSnicm const char *text; 97a42faf7dSnicm 98a42faf7dSnicm int expanded; 99a42faf7dSnicm int tagged; 10094c0d63cSnicm 10167c16a7cSnicm int draw_as_parent; 10294c0d63cSnicm int no_tag; 103a42faf7dSnicm 104a42faf7dSnicm struct mode_tree_list children; 105a42faf7dSnicm TAILQ_ENTRY(mode_tree_item) entry; 106a42faf7dSnicm }; 107a42faf7dSnicm 108a42faf7dSnicm struct mode_tree_line { 109a42faf7dSnicm struct mode_tree_item *item; 110a42faf7dSnicm u_int depth; 111a42faf7dSnicm int last; 112a42faf7dSnicm int flat; 113a42faf7dSnicm }; 114a42faf7dSnicm 115f43bc87cSnicm struct mode_tree_menu { 116f43bc87cSnicm struct mode_tree_data *data; 117f43bc87cSnicm struct client *c; 118f43bc87cSnicm u_int line; 119f43bc87cSnicm }; 120f43bc87cSnicm 121a42faf7dSnicm static void mode_tree_free_items(struct mode_tree_list *); 122a42faf7dSnicm 1231335341aSnicm static const struct menu_item mode_tree_menu_items[] = { 1241335341aSnicm { "Scroll Left", '<', NULL }, 1251335341aSnicm { "Scroll Right", '>', NULL }, 1261335341aSnicm { "", KEYC_NONE, NULL }, 1271335341aSnicm { "Cancel", 'q', NULL }, 1281335341aSnicm 1291335341aSnicm { NULL, KEYC_NONE, NULL } 1301335341aSnicm }; 131f43bc87cSnicm 132a42faf7dSnicm static struct mode_tree_item * 133a42faf7dSnicm mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag) 134a42faf7dSnicm { 135a42faf7dSnicm struct mode_tree_item *mti, *child; 136a42faf7dSnicm 137a42faf7dSnicm TAILQ_FOREACH(mti, mtl, entry) { 138a42faf7dSnicm if (mti->tag == tag) 139a42faf7dSnicm return (mti); 140a42faf7dSnicm child = mode_tree_find_item(&mti->children, tag); 141a42faf7dSnicm if (child != NULL) 142a42faf7dSnicm return (child); 143a42faf7dSnicm } 144a42faf7dSnicm return (NULL); 145a42faf7dSnicm } 146a42faf7dSnicm 147a42faf7dSnicm static void 148a42faf7dSnicm mode_tree_free_item(struct mode_tree_item *mti) 149a42faf7dSnicm { 150a42faf7dSnicm mode_tree_free_items(&mti->children); 151a42faf7dSnicm 152a42faf7dSnicm free((void *)mti->name); 153a42faf7dSnicm free((void *)mti->text); 154438eed14Snicm free((void *)mti->keystr); 155a42faf7dSnicm 156a42faf7dSnicm free(mti); 157a42faf7dSnicm } 158a42faf7dSnicm 159a42faf7dSnicm static void 160a42faf7dSnicm mode_tree_free_items(struct mode_tree_list *mtl) 161a42faf7dSnicm { 162a42faf7dSnicm struct mode_tree_item *mti, *mti1; 163a42faf7dSnicm 164a42faf7dSnicm TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) { 165a42faf7dSnicm TAILQ_REMOVE(mtl, mti, entry); 166a42faf7dSnicm mode_tree_free_item(mti); 167a42faf7dSnicm } 168a42faf7dSnicm } 169a42faf7dSnicm 170a42faf7dSnicm static void 1717e53ccf1Snicm mode_tree_check_selected(struct mode_tree_data *mtd) 1727e53ccf1Snicm { 1737e53ccf1Snicm /* 1747e53ccf1Snicm * If the current line would now be off screen reset the offset to the 1757e53ccf1Snicm * last visible line. 1767e53ccf1Snicm */ 1777e53ccf1Snicm if (mtd->current > mtd->height - 1) 1787e53ccf1Snicm mtd->offset = mtd->current - mtd->height + 1; 1797e53ccf1Snicm } 1807e53ccf1Snicm 1817e53ccf1Snicm static void 182a42faf7dSnicm mode_tree_clear_lines(struct mode_tree_data *mtd) 183a42faf7dSnicm { 184a42faf7dSnicm free(mtd->line_list); 185a42faf7dSnicm mtd->line_list = NULL; 186a42faf7dSnicm mtd->line_size = 0; 187a42faf7dSnicm } 188a42faf7dSnicm 189a42faf7dSnicm static void 190a42faf7dSnicm mode_tree_build_lines(struct mode_tree_data *mtd, 191a42faf7dSnicm struct mode_tree_list *mtl, u_int depth) 192a42faf7dSnicm { 193a42faf7dSnicm struct mode_tree_item *mti; 194a42faf7dSnicm struct mode_tree_line *line; 195a42faf7dSnicm u_int i; 196a42faf7dSnicm int flat = 1; 197a42faf7dSnicm 198a42faf7dSnicm mtd->depth = depth; 199a42faf7dSnicm TAILQ_FOREACH(mti, mtl, entry) { 200a42faf7dSnicm mtd->line_list = xreallocarray(mtd->line_list, 201a42faf7dSnicm mtd->line_size + 1, sizeof *mtd->line_list); 202a42faf7dSnicm 203a42faf7dSnicm line = &mtd->line_list[mtd->line_size++]; 204a42faf7dSnicm line->item = mti; 205a42faf7dSnicm line->depth = depth; 206a42faf7dSnicm line->last = (mti == TAILQ_LAST(mtl, mode_tree_list)); 207a42faf7dSnicm 208a42faf7dSnicm mti->line = (mtd->line_size - 1); 209a42faf7dSnicm if (!TAILQ_EMPTY(&mti->children)) 210a42faf7dSnicm flat = 0; 211a42faf7dSnicm if (mti->expanded) 212a42faf7dSnicm mode_tree_build_lines(mtd, &mti->children, depth + 1); 213438eed14Snicm 214438eed14Snicm if (mtd->keycb != NULL) { 215438eed14Snicm mti->key = mtd->keycb(mtd->modedata, mti->itemdata, 216438eed14Snicm mti->line); 217438eed14Snicm if (mti->key == KEYC_UNKNOWN) 218438eed14Snicm mti->key = KEYC_NONE; 219438eed14Snicm } else if (mti->line < 10) 220438eed14Snicm mti->key = '0' + mti->line; 221438eed14Snicm else if (mti->line < 36) 222438eed14Snicm mti->key = KEYC_META|('a' + mti->line - 10); 223438eed14Snicm else 224438eed14Snicm mti->key = KEYC_NONE; 225438eed14Snicm if (mti->key != KEYC_NONE) { 226438eed14Snicm mti->keystr = xstrdup(key_string_lookup_key(mti->key, 227438eed14Snicm 0)); 228438eed14Snicm mti->keylen = strlen(mti->keystr); 229438eed14Snicm } else { 230438eed14Snicm mti->keystr = NULL; 231438eed14Snicm mti->keylen = 0; 232438eed14Snicm } 233a42faf7dSnicm } 234a42faf7dSnicm TAILQ_FOREACH(mti, mtl, entry) { 235a42faf7dSnicm for (i = 0; i < mtd->line_size; i++) { 236a42faf7dSnicm line = &mtd->line_list[i]; 237a42faf7dSnicm if (line->item == mti) 238a42faf7dSnicm line->flat = flat; 239a42faf7dSnicm } 240a42faf7dSnicm } 241a42faf7dSnicm } 242a42faf7dSnicm 243a42faf7dSnicm static void 244a42faf7dSnicm mode_tree_clear_tagged(struct mode_tree_list *mtl) 245a42faf7dSnicm { 246a42faf7dSnicm struct mode_tree_item *mti; 247a42faf7dSnicm 248a42faf7dSnicm TAILQ_FOREACH(mti, mtl, entry) { 249a42faf7dSnicm mti->tagged = 0; 250a42faf7dSnicm mode_tree_clear_tagged(&mti->children); 251a42faf7dSnicm } 252a42faf7dSnicm } 253a42faf7dSnicm 25467c16a7cSnicm void 255a42faf7dSnicm mode_tree_up(struct mode_tree_data *mtd, int wrap) 256a42faf7dSnicm { 257a42faf7dSnicm if (mtd->current == 0) { 258a42faf7dSnicm if (wrap) { 259a42faf7dSnicm mtd->current = mtd->line_size - 1; 260a42faf7dSnicm if (mtd->line_size >= mtd->height) 261a42faf7dSnicm mtd->offset = mtd->line_size - mtd->height; 262a42faf7dSnicm } 263a42faf7dSnicm } else { 264a42faf7dSnicm mtd->current--; 265a42faf7dSnicm if (mtd->current < mtd->offset) 266a42faf7dSnicm mtd->offset--; 267a42faf7dSnicm } 268a42faf7dSnicm } 269a42faf7dSnicm 270d3278555Snicm int 271a42faf7dSnicm mode_tree_down(struct mode_tree_data *mtd, int wrap) 272a42faf7dSnicm { 273a42faf7dSnicm if (mtd->current == mtd->line_size - 1) { 274a42faf7dSnicm if (wrap) { 275a42faf7dSnicm mtd->current = 0; 276a42faf7dSnicm mtd->offset = 0; 277d3278555Snicm } else 278d3278555Snicm return (0); 279a42faf7dSnicm } else { 280a42faf7dSnicm mtd->current++; 281a42faf7dSnicm if (mtd->current > mtd->offset + mtd->height - 1) 282a42faf7dSnicm mtd->offset++; 283a42faf7dSnicm } 284d3278555Snicm return (1); 285a42faf7dSnicm } 286a42faf7dSnicm 287a42faf7dSnicm void * 288a42faf7dSnicm mode_tree_get_current(struct mode_tree_data *mtd) 289a42faf7dSnicm { 290a42faf7dSnicm return (mtd->line_list[mtd->current].item->itemdata); 291a42faf7dSnicm } 292a42faf7dSnicm 29367c16a7cSnicm const char * 29467c16a7cSnicm mode_tree_get_current_name(struct mode_tree_data *mtd) 29567c16a7cSnicm { 29667c16a7cSnicm return (mtd->line_list[mtd->current].item->name); 29767c16a7cSnicm } 29867c16a7cSnicm 29963c9949dSnicm void 30063c9949dSnicm mode_tree_expand_current(struct mode_tree_data *mtd) 30163c9949dSnicm { 30263c9949dSnicm if (!mtd->line_list[mtd->current].item->expanded) { 30363c9949dSnicm mtd->line_list[mtd->current].item->expanded = 1; 30463c9949dSnicm mode_tree_build(mtd); 30563c9949dSnicm } 30663c9949dSnicm } 30763c9949dSnicm 30867c16a7cSnicm void 30967c16a7cSnicm mode_tree_collapse_current(struct mode_tree_data *mtd) 31067c16a7cSnicm { 31167c16a7cSnicm if (mtd->line_list[mtd->current].item->expanded) { 31267c16a7cSnicm mtd->line_list[mtd->current].item->expanded = 0; 31367c16a7cSnicm mode_tree_build(mtd); 31467c16a7cSnicm } 31567c16a7cSnicm } 31667c16a7cSnicm 3177d0fd907Snicm static int 3187d0fd907Snicm mode_tree_get_tag(struct mode_tree_data *mtd, uint64_t tag, u_int *found) 31963c9949dSnicm { 32063c9949dSnicm u_int i; 32163c9949dSnicm 32263c9949dSnicm for (i = 0; i < mtd->line_size; i++) { 32363c9949dSnicm if (mtd->line_list[i].item->tag == tag) 32463c9949dSnicm break; 32563c9949dSnicm } 32663c9949dSnicm if (i != mtd->line_size) { 3277d0fd907Snicm *found = i; 3287d0fd907Snicm return (1); 3297d0fd907Snicm } 3307d0fd907Snicm return (0); 3317d0fd907Snicm } 3327d0fd907Snicm 3337d0fd907Snicm void 3347d0fd907Snicm mode_tree_expand(struct mode_tree_data *mtd, uint64_t tag) 3357d0fd907Snicm { 3367d0fd907Snicm u_int found; 3377d0fd907Snicm 3387d0fd907Snicm if (!mode_tree_get_tag(mtd, tag, &found)) 3397d0fd907Snicm return; 3407d0fd907Snicm if (!mtd->line_list[found].item->expanded) { 3417d0fd907Snicm mtd->line_list[found].item->expanded = 1; 3427d0fd907Snicm mode_tree_build(mtd); 3437d0fd907Snicm } 3447d0fd907Snicm } 3457d0fd907Snicm 3467d0fd907Snicm int 3477d0fd907Snicm mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag) 3487d0fd907Snicm { 3497d0fd907Snicm u_int found; 3507d0fd907Snicm 3517d0fd907Snicm if (mode_tree_get_tag(mtd, tag, &found)) { 3527d0fd907Snicm mtd->current = found; 35363c9949dSnicm if (mtd->current > mtd->height - 1) 35463c9949dSnicm mtd->offset = mtd->current - mtd->height + 1; 35563c9949dSnicm else 35663c9949dSnicm mtd->offset = 0; 3577d0fd907Snicm return (1); 3587d0fd907Snicm } 35982325b0dSnicm if (mtd->current >= mtd->line_size) { 36082325b0dSnicm mtd->current = mtd->line_size - 1; 36182325b0dSnicm if (mtd->current > mtd->height - 1) 36282325b0dSnicm mtd->offset = mtd->current - mtd->height + 1; 36382325b0dSnicm else 36463c9949dSnicm mtd->offset = 0; 36582325b0dSnicm } 3667d0fd907Snicm return (0); 36763c9949dSnicm } 36863c9949dSnicm 369a42faf7dSnicm u_int 370a42faf7dSnicm mode_tree_count_tagged(struct mode_tree_data *mtd) 371a42faf7dSnicm { 372a42faf7dSnicm struct mode_tree_item *mti; 373a42faf7dSnicm u_int i, tagged; 374a42faf7dSnicm 375a42faf7dSnicm tagged = 0; 376a42faf7dSnicm for (i = 0; i < mtd->line_size; i++) { 377a42faf7dSnicm mti = mtd->line_list[i].item; 378a42faf7dSnicm if (mti->tagged) 379a42faf7dSnicm tagged++; 380a42faf7dSnicm } 381a42faf7dSnicm return (tagged); 382a42faf7dSnicm } 383a42faf7dSnicm 384a42faf7dSnicm void 3852b7e51f7Snicm mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb, 3862b7e51f7Snicm struct client *c, key_code key, int current) 387a42faf7dSnicm { 388a42faf7dSnicm struct mode_tree_item *mti; 389a42faf7dSnicm u_int i; 390a42faf7dSnicm int fired; 391a42faf7dSnicm 392a42faf7dSnicm fired = 0; 393a42faf7dSnicm for (i = 0; i < mtd->line_size; i++) { 394a42faf7dSnicm mti = mtd->line_list[i].item; 395a42faf7dSnicm if (mti->tagged) { 396a42faf7dSnicm fired = 1; 397d7af2c28Snicm cb(mtd->modedata, mti->itemdata, c, key); 398a42faf7dSnicm } 399a42faf7dSnicm } 400a42faf7dSnicm if (!fired && current) { 401a42faf7dSnicm mti = mtd->line_list[mtd->current].item; 402d7af2c28Snicm cb(mtd->modedata, mti->itemdata, c, key); 403a42faf7dSnicm } 404a42faf7dSnicm } 405a42faf7dSnicm 406a42faf7dSnicm struct mode_tree_data * 407b38aa712Snicm mode_tree_start(struct window_pane *wp, struct args *args, 4082b7e51f7Snicm mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb, 40967c16a7cSnicm mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, 410438eed14Snicm mode_tree_height_cb heightcb, mode_tree_key_cb keycb, void *modedata, 4111335341aSnicm const struct menu_item *menu, const char **sort_list, u_int sort_size, 412f43bc87cSnicm struct screen **s) 413a42faf7dSnicm { 414a42faf7dSnicm struct mode_tree_data *mtd; 415b38aa712Snicm const char *sort; 416b38aa712Snicm u_int i; 417a42faf7dSnicm 418a42faf7dSnicm mtd = xcalloc(1, sizeof *mtd); 419943a08b1Snicm mtd->references = 1; 420943a08b1Snicm 421a42faf7dSnicm mtd->wp = wp; 422a42faf7dSnicm mtd->modedata = modedata; 423f43bc87cSnicm mtd->menu = menu; 424a42faf7dSnicm 425a42faf7dSnicm mtd->sort_list = sort_list; 426a42faf7dSnicm mtd->sort_size = sort_size; 427a42faf7dSnicm 42875c5b1bcSnicm if (args_has(args, 'N') > 1) 42975c5b1bcSnicm mtd->preview = MODE_TREE_PREVIEW_BIG; 43075c5b1bcSnicm else if (args_has(args, 'N')) 43175c5b1bcSnicm mtd->preview = MODE_TREE_PREVIEW_OFF; 43275c5b1bcSnicm else 43375c5b1bcSnicm mtd->preview = MODE_TREE_PREVIEW_NORMAL; 434f84988b9Snicm 435b38aa712Snicm sort = args_get(args, 'O'); 436b38aa712Snicm if (sort != NULL) { 437b38aa712Snicm for (i = 0; i < sort_size; i++) { 438b38aa712Snicm if (strcasecmp(sort, sort_list[i]) == 0) 4393f6f9f7dSnicm mtd->sort_crit.field = i; 440b38aa712Snicm } 441b38aa712Snicm } 4423f6f9f7dSnicm mtd->sort_crit.reversed = args_has(args, 'r'); 443b38aa712Snicm 444024c311aSnicm if (args_has(args, 'f')) 445024c311aSnicm mtd->filter = xstrdup(args_get(args, 'f')); 446024c311aSnicm else 447024c311aSnicm mtd->filter = NULL; 448024c311aSnicm 449a42faf7dSnicm mtd->buildcb = buildcb; 450a42faf7dSnicm mtd->drawcb = drawcb; 451943a08b1Snicm mtd->searchcb = searchcb; 452f43bc87cSnicm mtd->menucb = menucb; 45367c16a7cSnicm mtd->heightcb = heightcb; 454438eed14Snicm mtd->keycb = keycb; 455a42faf7dSnicm 456a42faf7dSnicm TAILQ_INIT(&mtd->children); 457a42faf7dSnicm 458a42faf7dSnicm *s = &mtd->screen; 459a42faf7dSnicm screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); 460a42faf7dSnicm (*s)->mode &= ~MODE_CURSOR; 461a42faf7dSnicm 462a42faf7dSnicm return (mtd); 463a42faf7dSnicm } 464a42faf7dSnicm 465a42faf7dSnicm void 4664f5e4c93Snicm mode_tree_zoom(struct mode_tree_data *mtd, struct args *args) 4674f5e4c93Snicm { 4684f5e4c93Snicm struct window_pane *wp = mtd->wp; 4694f5e4c93Snicm 4704f5e4c93Snicm if (args_has(args, 'Z')) { 4714f5e4c93Snicm mtd->zoomed = (wp->window->flags & WINDOW_ZOOMED); 4724f5e4c93Snicm if (!mtd->zoomed && window_zoom(wp) == 0) 4734f5e4c93Snicm server_redraw_window(wp->window); 4744f5e4c93Snicm } else 4754f5e4c93Snicm mtd->zoomed = -1; 4764f5e4c93Snicm } 4774f5e4c93Snicm 47867c16a7cSnicm static void 47967c16a7cSnicm mode_tree_set_height(struct mode_tree_data *mtd) 48067c16a7cSnicm { 48167c16a7cSnicm struct screen *s = &mtd->screen; 48267c16a7cSnicm u_int height; 48367c16a7cSnicm 48467c16a7cSnicm if (mtd->heightcb != NULL) { 48567c16a7cSnicm height = mtd->heightcb(mtd, screen_size_y(s)); 48667c16a7cSnicm if (height < screen_size_y(s)) 48767c16a7cSnicm mtd->height = screen_size_y(s) - height; 48867c16a7cSnicm } else { 48975c5b1bcSnicm if (mtd->preview == MODE_TREE_PREVIEW_NORMAL) { 49067c16a7cSnicm mtd->height = (screen_size_y(s) / 3) * 2; 49167c16a7cSnicm if (mtd->height > mtd->line_size) 49267c16a7cSnicm mtd->height = screen_size_y(s) / 2; 49367c16a7cSnicm if (mtd->height < 10) 49467c16a7cSnicm mtd->height = screen_size_y(s); 49575c5b1bcSnicm } else if (mtd->preview == MODE_TREE_PREVIEW_BIG) { 49675c5b1bcSnicm mtd->height = screen_size_y(s) / 4; 49775c5b1bcSnicm if (mtd->height > mtd->line_size) 49875c5b1bcSnicm mtd->height = mtd->line_size; 49975c5b1bcSnicm if (mtd->height < 2) 50075c5b1bcSnicm mtd->height = 2; 50175c5b1bcSnicm } else 50275c5b1bcSnicm mtd->height = screen_size_y(s); 50375c5b1bcSnicm } 50467c16a7cSnicm if (screen_size_y(s) - mtd->height < 2) 50567c16a7cSnicm mtd->height = screen_size_y(s); 50667c16a7cSnicm } 50767c16a7cSnicm 5084f5e4c93Snicm void 509a42faf7dSnicm mode_tree_build(struct mode_tree_data *mtd) 510a42faf7dSnicm { 511a42faf7dSnicm struct screen *s = &mtd->screen; 512a42faf7dSnicm uint64_t tag; 513a42faf7dSnicm 514a42faf7dSnicm if (mtd->line_list != NULL) 515a42faf7dSnicm tag = mtd->line_list[mtd->current].item->tag; 516a42faf7dSnicm else 517848da443Snicm tag = UINT64_MAX; 518a42faf7dSnicm 519a42faf7dSnicm TAILQ_CONCAT(&mtd->saved, &mtd->children, entry); 520a42faf7dSnicm TAILQ_INIT(&mtd->children); 521a42faf7dSnicm 5223f6f9f7dSnicm mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter); 5234d1af203Snicm mtd->no_matches = TAILQ_EMPTY(&mtd->children); 5244d1af203Snicm if (mtd->no_matches) 5253f6f9f7dSnicm mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, NULL); 526a42faf7dSnicm 527a42faf7dSnicm mode_tree_free_items(&mtd->saved); 528a42faf7dSnicm TAILQ_INIT(&mtd->saved); 529a42faf7dSnicm 530a42faf7dSnicm mode_tree_clear_lines(mtd); 531a42faf7dSnicm mode_tree_build_lines(mtd, &mtd->children, 0); 532a42faf7dSnicm 5333ece9b2cSnicm if (mtd->line_list != NULL && tag == UINT64_MAX) 534848da443Snicm tag = mtd->line_list[mtd->current].item->tag; 535943a08b1Snicm mode_tree_set_current(mtd, tag); 536a42faf7dSnicm 537a42faf7dSnicm mtd->width = screen_size_x(s); 53875c5b1bcSnicm if (mtd->preview != MODE_TREE_PREVIEW_OFF) 53967c16a7cSnicm mode_tree_set_height(mtd); 54067c16a7cSnicm else 541f84988b9Snicm mtd->height = screen_size_y(s); 5427e53ccf1Snicm mode_tree_check_selected(mtd); 543a42faf7dSnicm } 544a42faf7dSnicm 545943a08b1Snicm static void 546943a08b1Snicm mode_tree_remove_ref(struct mode_tree_data *mtd) 547943a08b1Snicm { 548943a08b1Snicm if (--mtd->references == 0) 549943a08b1Snicm free(mtd); 550943a08b1Snicm } 551943a08b1Snicm 552a42faf7dSnicm void 553a42faf7dSnicm mode_tree_free(struct mode_tree_data *mtd) 554a42faf7dSnicm { 5554f5e4c93Snicm struct window_pane *wp = mtd->wp; 5564f5e4c93Snicm 5574f5e4c93Snicm if (mtd->zoomed == 0) 5584f5e4c93Snicm server_unzoom_window(wp->window); 5594f5e4c93Snicm 560a42faf7dSnicm mode_tree_free_items(&mtd->children); 561a42faf7dSnicm mode_tree_clear_lines(mtd); 562a42faf7dSnicm screen_free(&mtd->screen); 563943a08b1Snicm 564024c311aSnicm free(mtd->search); 565024c311aSnicm free(mtd->filter); 566024c311aSnicm 567943a08b1Snicm mtd->dead = 1; 568943a08b1Snicm mode_tree_remove_ref(mtd); 569a42faf7dSnicm } 570a42faf7dSnicm 571a42faf7dSnicm void 572a42faf7dSnicm mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy) 573a42faf7dSnicm { 574a42faf7dSnicm struct screen *s = &mtd->screen; 575a42faf7dSnicm 576a42faf7dSnicm screen_resize(s, sx, sy, 0); 577a42faf7dSnicm 578a42faf7dSnicm mode_tree_build(mtd); 579a42faf7dSnicm mode_tree_draw(mtd); 580a42faf7dSnicm 581a42faf7dSnicm mtd->wp->flags |= PANE_REDRAW; 582a42faf7dSnicm } 583a42faf7dSnicm 584a42faf7dSnicm struct mode_tree_item * 585a42faf7dSnicm mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent, 586a42faf7dSnicm void *itemdata, uint64_t tag, const char *name, const char *text, 587a42faf7dSnicm int expanded) 588a42faf7dSnicm { 589a42faf7dSnicm struct mode_tree_item *mti, *saved; 590a42faf7dSnicm 591a42faf7dSnicm log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag, 59267c16a7cSnicm name, (text == NULL ? "" : text)); 593a42faf7dSnicm 594a42faf7dSnicm mti = xcalloc(1, sizeof *mti); 595a42faf7dSnicm mti->parent = parent; 596a42faf7dSnicm mti->itemdata = itemdata; 597a42faf7dSnicm 598a42faf7dSnicm mti->tag = tag; 599a42faf7dSnicm mti->name = xstrdup(name); 60067c16a7cSnicm if (text != NULL) 601a42faf7dSnicm mti->text = xstrdup(text); 602a42faf7dSnicm 603a42faf7dSnicm saved = mode_tree_find_item(&mtd->saved, tag); 604a42faf7dSnicm if (saved != NULL) { 60510b50ca4Snicm if (parent == NULL || parent->expanded) 606a42faf7dSnicm mti->tagged = saved->tagged; 607a42faf7dSnicm mti->expanded = saved->expanded; 608a42faf7dSnicm } else if (expanded == -1) 609a42faf7dSnicm mti->expanded = 1; 610a42faf7dSnicm else 611a42faf7dSnicm mti->expanded = expanded; 612a42faf7dSnicm 613a42faf7dSnicm TAILQ_INIT(&mti->children); 614a42faf7dSnicm 615a42faf7dSnicm if (parent != NULL) 616a42faf7dSnicm TAILQ_INSERT_TAIL(&parent->children, mti, entry); 617a42faf7dSnicm else 618a42faf7dSnicm TAILQ_INSERT_TAIL(&mtd->children, mti, entry); 619a42faf7dSnicm 620a42faf7dSnicm return (mti); 621a42faf7dSnicm } 622a42faf7dSnicm 623a42faf7dSnicm void 62467c16a7cSnicm mode_tree_draw_as_parent(struct mode_tree_item *mti) 62567c16a7cSnicm { 62667c16a7cSnicm mti->draw_as_parent = 1; 62767c16a7cSnicm } 62867c16a7cSnicm 62967c16a7cSnicm void 63094c0d63cSnicm mode_tree_no_tag(struct mode_tree_item *mti) 63194c0d63cSnicm { 63294c0d63cSnicm mti->no_tag = 1; 63394c0d63cSnicm } 63494c0d63cSnicm 63594c0d63cSnicm void 636a42faf7dSnicm mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti) 637a42faf7dSnicm { 638a42faf7dSnicm struct mode_tree_item *parent = mti->parent; 639a42faf7dSnicm 640a42faf7dSnicm if (parent != NULL) 641a42faf7dSnicm TAILQ_REMOVE(&parent->children, mti, entry); 642a42faf7dSnicm else 643a42faf7dSnicm TAILQ_REMOVE(&mtd->children, mti, entry); 644a42faf7dSnicm mode_tree_free_item(mti); 645a42faf7dSnicm } 646a42faf7dSnicm 647a42faf7dSnicm void 648a42faf7dSnicm mode_tree_draw(struct mode_tree_data *mtd) 649a42faf7dSnicm { 650a42faf7dSnicm struct window_pane *wp = mtd->wp; 6512b7e51f7Snicm struct screen *s = &mtd->screen; 652a42faf7dSnicm struct mode_tree_line *line; 653a42faf7dSnicm struct mode_tree_item *mti; 654a42faf7dSnicm struct options *oo = wp->window->options; 655a42faf7dSnicm struct screen_write_ctx ctx; 656a42faf7dSnicm struct grid_cell gc0, gc; 6574ffcb1c8Snicm u_int w, h, i, j, sy, box_x, box_y, width; 658438eed14Snicm char *text, *start, *key; 659a42faf7dSnicm const char *tag, *symbol; 6604d1af203Snicm size_t size, n; 661438eed14Snicm int keylen, pad; 662a42faf7dSnicm 663a42faf7dSnicm if (mtd->line_size == 0) 664a42faf7dSnicm return; 665a42faf7dSnicm 666a42faf7dSnicm memcpy(&gc0, &grid_default_cell, sizeof gc0); 667a42faf7dSnicm memcpy(&gc, &grid_default_cell, sizeof gc); 66801c0c428Snicm style_apply(&gc, oo, "mode-style", NULL); 669a42faf7dSnicm 670a42faf7dSnicm w = mtd->width; 671a42faf7dSnicm h = mtd->height; 672a42faf7dSnicm 67383e83a91Snicm screen_write_start(&ctx, s); 674a42faf7dSnicm screen_write_clearscreen(&ctx, 8); 675a42faf7dSnicm 676438eed14Snicm keylen = 0; 677438eed14Snicm for (i = 0; i < mtd->line_size; i++) { 678438eed14Snicm mti = mtd->line_list[i].item; 679438eed14Snicm if (mti->key == KEYC_NONE) 680438eed14Snicm continue; 681438eed14Snicm if ((int)mti->keylen + 3 > keylen) 682438eed14Snicm keylen = mti->keylen + 3; 683438eed14Snicm } 684a42faf7dSnicm 685a42faf7dSnicm for (i = 0; i < mtd->line_size; i++) { 686a42faf7dSnicm if (i < mtd->offset) 687a42faf7dSnicm continue; 688a42faf7dSnicm if (i > mtd->offset + h - 1) 689a42faf7dSnicm break; 690a42faf7dSnicm line = &mtd->line_list[i]; 691a42faf7dSnicm mti = line->item; 692a42faf7dSnicm 6935c6c7001Snicm screen_write_cursormove(&ctx, 0, i - mtd->offset, 0); 694a42faf7dSnicm 695438eed14Snicm pad = keylen - 2 - mti->keylen; 696438eed14Snicm if (mti->key != KEYC_NONE) 697438eed14Snicm xasprintf(&key, "(%s)%*s", mti->keystr, pad, ""); 698a42faf7dSnicm else 699438eed14Snicm key = xstrdup(""); 700a42faf7dSnicm 701a42faf7dSnicm if (line->flat) 702a42faf7dSnicm symbol = ""; 703a42faf7dSnicm else if (TAILQ_EMPTY(&mti->children)) 704a42faf7dSnicm symbol = " "; 705a42faf7dSnicm else if (mti->expanded) 706a42faf7dSnicm symbol = "- "; 707a42faf7dSnicm else 708a42faf7dSnicm symbol = "+ "; 709a42faf7dSnicm 710a42faf7dSnicm if (line->depth == 0) 711a42faf7dSnicm start = xstrdup(symbol); 712a42faf7dSnicm else { 713a42faf7dSnicm size = (4 * line->depth) + 32; 714a42faf7dSnicm 715a42faf7dSnicm start = xcalloc(1, size); 716a42faf7dSnicm for (j = 1; j < line->depth; j++) { 717a42faf7dSnicm if (mti->parent != NULL && 718a42faf7dSnicm mtd->line_list[mti->parent->line].last) 719a42faf7dSnicm strlcat(start, " ", size); 720a42faf7dSnicm else 721a42faf7dSnicm strlcat(start, "\001x\001 ", size); 722a42faf7dSnicm } 723a42faf7dSnicm if (line->last) 724a42faf7dSnicm strlcat(start, "\001mq\001> ", size); 725a42faf7dSnicm else 726a42faf7dSnicm strlcat(start, "\001tq\001> ", size); 727a42faf7dSnicm strlcat(start, symbol, size); 728a42faf7dSnicm } 729a42faf7dSnicm 730a42faf7dSnicm if (mti->tagged) 731a42faf7dSnicm tag = "*"; 732a42faf7dSnicm else 733a42faf7dSnicm tag = ""; 73467c16a7cSnicm xasprintf(&text, "%-*s%s%s%s%s", keylen, key, start, mti->name, 73567c16a7cSnicm tag, (mti->text != NULL) ? ": " : "" ); 7364ffcb1c8Snicm width = utf8_cstrwidth(text); 73738f16556Snicm if (width > w) 73838f16556Snicm width = w; 739a42faf7dSnicm free(start); 740a42faf7dSnicm 741a42faf7dSnicm if (mti->tagged) { 742a42faf7dSnicm gc.attr ^= GRID_ATTR_BRIGHT; 743a42faf7dSnicm gc0.attr ^= GRID_ATTR_BRIGHT; 744a42faf7dSnicm } 745a42faf7dSnicm 746a42faf7dSnicm if (i != mtd->current) { 747a42faf7dSnicm screen_write_clearendofline(&ctx, 8); 74838f16556Snicm screen_write_nputs(&ctx, w, &gc0, "%s", text); 74967c16a7cSnicm if (mti->text != NULL) { 75067c16a7cSnicm format_draw(&ctx, &gc0, w - width, mti->text, 751173e8225Snicm NULL, 0); 75267c16a7cSnicm } 753c48b7ec2Snicm } else { 754c48b7ec2Snicm screen_write_clearendofline(&ctx, gc.bg); 75538f16556Snicm screen_write_nputs(&ctx, w, &gc, "%s", text); 75667c16a7cSnicm if (mti->text != NULL) { 75767c16a7cSnicm format_draw(&ctx, &gc, w - width, mti->text, 758173e8225Snicm NULL, 0); 75967c16a7cSnicm } 760c48b7ec2Snicm } 761a42faf7dSnicm free(text); 762438eed14Snicm free(key); 763a42faf7dSnicm 764a42faf7dSnicm if (mti->tagged) { 765a42faf7dSnicm gc.attr ^= GRID_ATTR_BRIGHT; 766a42faf7dSnicm gc0.attr ^= GRID_ATTR_BRIGHT; 767a42faf7dSnicm } 768a42faf7dSnicm } 769a42faf7dSnicm 77075c5b1bcSnicm if (mtd->preview == MODE_TREE_PREVIEW_OFF) 77175c5b1bcSnicm goto done; 77275c5b1bcSnicm 773a42faf7dSnicm sy = screen_size_y(s); 77475c5b1bcSnicm if (sy <= 4 || h < 2 || sy - h <= 4 || w <= 4) 77506d8632fSnicm goto done; 776a42faf7dSnicm 777a42faf7dSnicm line = &mtd->line_list[mtd->current]; 778a42faf7dSnicm mti = line->item; 77967c16a7cSnicm if (mti->draw_as_parent) 78067c16a7cSnicm mti = mti->parent; 781a42faf7dSnicm 7825c6c7001Snicm screen_write_cursormove(&ctx, 0, h, 0); 783dad8e5c0Snicm screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL, NULL); 784a42faf7dSnicm 78567c16a7cSnicm if (mtd->sort_list != NULL) { 7863f6f9f7dSnicm xasprintf(&text, " %s (sort: %s%s)", mti->name, 7873f6f9f7dSnicm mtd->sort_list[mtd->sort_crit.field], 7883f6f9f7dSnicm mtd->sort_crit.reversed ? ", reversed" : ""); 78967c16a7cSnicm } else 79067c16a7cSnicm xasprintf(&text, " %s", mti->name); 791a42faf7dSnicm if (w - 2 >= strlen(text)) { 7925c6c7001Snicm screen_write_cursormove(&ctx, 1, h, 0); 793a42faf7dSnicm screen_write_puts(&ctx, &gc0, "%s", text); 7944d1af203Snicm 7954d1af203Snicm if (mtd->no_matches) 7964d1af203Snicm n = (sizeof "no matches") - 1; 7974d1af203Snicm else 7984d1af203Snicm n = (sizeof "active") - 1; 7994d1af203Snicm if (mtd->filter != NULL && w - 2 >= strlen(text) + 10 + n + 2) { 8004d1af203Snicm screen_write_puts(&ctx, &gc0, " (filter: "); 8014d1af203Snicm if (mtd->no_matches) 8024d1af203Snicm screen_write_puts(&ctx, &gc, "no matches"); 8034d1af203Snicm else 8044d1af203Snicm screen_write_puts(&ctx, &gc0, "active"); 8054d1af203Snicm screen_write_puts(&ctx, &gc0, ") "); 80667c16a7cSnicm } else 80767c16a7cSnicm screen_write_puts(&ctx, &gc0, " "); 808a42faf7dSnicm } 809a42faf7dSnicm free(text); 810a42faf7dSnicm 811a42faf7dSnicm box_x = w - 4; 812a42faf7dSnicm box_y = sy - h - 2; 813a42faf7dSnicm 8142b7e51f7Snicm if (box_x != 0 && box_y != 0) { 8155c6c7001Snicm screen_write_cursormove(&ctx, 2, h + 1, 0); 8162b7e51f7Snicm mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y); 817a42faf7dSnicm } 818a42faf7dSnicm 81906d8632fSnicm done: 82006d8632fSnicm screen_write_cursormove(&ctx, 0, mtd->current - mtd->offset, 0); 821a42faf7dSnicm screen_write_stop(&ctx); 822a42faf7dSnicm } 823a42faf7dSnicm 824943a08b1Snicm static struct mode_tree_item * 825cea02a81Snicm mode_tree_search_backward(struct mode_tree_data *mtd) 826cea02a81Snicm { 827cea02a81Snicm struct mode_tree_item *mti, *last, *prev; 828cea02a81Snicm 829cea02a81Snicm if (mtd->search == NULL) 830cea02a81Snicm return (NULL); 831cea02a81Snicm 832cea02a81Snicm mti = last = mtd->line_list[mtd->current].item; 833cea02a81Snicm for (;;) { 834cea02a81Snicm if ((prev = TAILQ_PREV(mti, mode_tree_list, entry)) != NULL) { 835cea02a81Snicm /* Point to the last child in the previous subtree. */ 836cea02a81Snicm while (!TAILQ_EMPTY(&prev->children)) 837cea02a81Snicm prev = TAILQ_LAST(&prev->children, mode_tree_list); 838cea02a81Snicm mti = prev; 839cea02a81Snicm } else { 840cea02a81Snicm /* If prev is NULL, jump to the parent. */ 841cea02a81Snicm mti = mti->parent; 842cea02a81Snicm } 843cea02a81Snicm 844cea02a81Snicm if (mti == NULL) { 845cea02a81Snicm /* Point to the last child in the last root subtree. */ 846cea02a81Snicm prev = TAILQ_LAST(&mtd->children, mode_tree_list); 847cea02a81Snicm while (!TAILQ_EMPTY(&prev->children)) 848cea02a81Snicm prev = TAILQ_LAST(&prev->children, mode_tree_list); 849cea02a81Snicm mti = prev; 850cea02a81Snicm } 851cea02a81Snicm if (mti == last) 852cea02a81Snicm break; 853cea02a81Snicm 854cea02a81Snicm if (mtd->searchcb == NULL) { 855cea02a81Snicm if (strstr(mti->name, mtd->search) != NULL) 856cea02a81Snicm return (mti); 857cea02a81Snicm continue; 858cea02a81Snicm } 859cea02a81Snicm if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search)) 860cea02a81Snicm return (mti); 861cea02a81Snicm } 862cea02a81Snicm return (NULL); 863cea02a81Snicm } 864cea02a81Snicm 865cea02a81Snicm 866cea02a81Snicm static struct mode_tree_item * 867cea02a81Snicm mode_tree_search_forward(struct mode_tree_data *mtd) 868943a08b1Snicm { 869943a08b1Snicm struct mode_tree_item *mti, *last, *next; 870943a08b1Snicm 871024c311aSnicm if (mtd->search == NULL) 872943a08b1Snicm return (NULL); 873943a08b1Snicm 874943a08b1Snicm mti = last = mtd->line_list[mtd->current].item; 875943a08b1Snicm for (;;) { 876943a08b1Snicm if (!TAILQ_EMPTY(&mti->children)) 877943a08b1Snicm mti = TAILQ_FIRST(&mti->children); 878943a08b1Snicm else if ((next = TAILQ_NEXT(mti, entry)) != NULL) 879943a08b1Snicm mti = next; 880943a08b1Snicm else { 881943a08b1Snicm for (;;) { 882943a08b1Snicm mti = mti->parent; 883943a08b1Snicm if (mti == NULL) 884943a08b1Snicm break; 885943a08b1Snicm if ((next = TAILQ_NEXT(mti, entry)) != NULL) { 886943a08b1Snicm mti = next; 887943a08b1Snicm break; 888943a08b1Snicm } 889943a08b1Snicm } 890943a08b1Snicm } 891943a08b1Snicm if (mti == NULL) 892943a08b1Snicm mti = TAILQ_FIRST(&mtd->children); 893943a08b1Snicm if (mti == last) 894943a08b1Snicm break; 895943a08b1Snicm 896943a08b1Snicm if (mtd->searchcb == NULL) { 897024c311aSnicm if (strstr(mti->name, mtd->search) != NULL) 898943a08b1Snicm return (mti); 899943a08b1Snicm continue; 900943a08b1Snicm } 901024c311aSnicm if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search)) 902943a08b1Snicm return (mti); 903943a08b1Snicm } 904943a08b1Snicm return (NULL); 905943a08b1Snicm } 906943a08b1Snicm 907943a08b1Snicm static void 908943a08b1Snicm mode_tree_search_set(struct mode_tree_data *mtd) 909943a08b1Snicm { 910943a08b1Snicm struct mode_tree_item *mti, *loop; 911943a08b1Snicm uint64_t tag; 912943a08b1Snicm 913cea02a81Snicm if (mtd->search_dir == MODE_TREE_SEARCH_FORWARD) 914cea02a81Snicm mti = mode_tree_search_forward(mtd); 915cea02a81Snicm else 916cea02a81Snicm mti = mode_tree_search_backward(mtd); 917943a08b1Snicm if (mti == NULL) 918943a08b1Snicm return; 919943a08b1Snicm tag = mti->tag; 920943a08b1Snicm 921943a08b1Snicm loop = mti->parent; 922943a08b1Snicm while (loop != NULL) { 923943a08b1Snicm loop->expanded = 1; 924943a08b1Snicm loop = loop->parent; 925943a08b1Snicm } 926943a08b1Snicm 927024c311aSnicm mode_tree_build(mtd); 928943a08b1Snicm mode_tree_set_current(mtd, tag); 929943a08b1Snicm mode_tree_draw(mtd); 930024c311aSnicm mtd->wp->flags |= PANE_REDRAW; 931943a08b1Snicm } 932943a08b1Snicm 933943a08b1Snicm static int 934943a08b1Snicm mode_tree_search_callback(__unused struct client *c, void *data, const char *s, 935943a08b1Snicm __unused int done) 936943a08b1Snicm { 937943a08b1Snicm struct mode_tree_data *mtd = data; 938943a08b1Snicm 939943a08b1Snicm if (mtd->dead) 940943a08b1Snicm return (0); 941943a08b1Snicm 942024c311aSnicm free(mtd->search); 943024c311aSnicm if (s == NULL || *s == '\0') { 944024c311aSnicm mtd->search = NULL; 945943a08b1Snicm return (0); 946943a08b1Snicm } 947024c311aSnicm mtd->search = xstrdup(s); 948943a08b1Snicm mode_tree_search_set(mtd); 949943a08b1Snicm 950943a08b1Snicm return (0); 951943a08b1Snicm } 952943a08b1Snicm 953943a08b1Snicm static void 954943a08b1Snicm mode_tree_search_free(void *data) 955943a08b1Snicm { 956943a08b1Snicm mode_tree_remove_ref(data); 957943a08b1Snicm } 958943a08b1Snicm 959024c311aSnicm static int 960024c311aSnicm mode_tree_filter_callback(__unused struct client *c, void *data, const char *s, 961024c311aSnicm __unused int done) 962024c311aSnicm { 963024c311aSnicm struct mode_tree_data *mtd = data; 964024c311aSnicm 965024c311aSnicm if (mtd->dead) 966024c311aSnicm return (0); 967024c311aSnicm 968024c311aSnicm if (mtd->filter != NULL) 969024c311aSnicm free(mtd->filter); 970024c311aSnicm if (s == NULL || *s == '\0') 971024c311aSnicm mtd->filter = NULL; 972024c311aSnicm else 973024c311aSnicm mtd->filter = xstrdup(s); 974024c311aSnicm 975024c311aSnicm mode_tree_build(mtd); 976024c311aSnicm mode_tree_draw(mtd); 977024c311aSnicm mtd->wp->flags |= PANE_REDRAW; 978024c311aSnicm 979024c311aSnicm return (0); 980024c311aSnicm } 981024c311aSnicm 982024c311aSnicm static void 983024c311aSnicm mode_tree_filter_free(void *data) 984024c311aSnicm { 985024c311aSnicm mode_tree_remove_ref(data); 986024c311aSnicm } 987024c311aSnicm 988f43bc87cSnicm static void 989f43bc87cSnicm mode_tree_menu_callback(__unused struct menu *menu, __unused u_int idx, 990f43bc87cSnicm key_code key, void *data) 991f43bc87cSnicm { 992f43bc87cSnicm struct mode_tree_menu *mtm = data; 993f43bc87cSnicm struct mode_tree_data *mtd = mtm->data; 994f43bc87cSnicm 995f43bc87cSnicm if (mtd->dead || key == KEYC_NONE) 996f43bc87cSnicm goto out; 997f43bc87cSnicm 998f43bc87cSnicm if (mtm->line >= mtd->line_size) 999f43bc87cSnicm goto out; 1000f43bc87cSnicm mtd->current = mtm->line; 1001f43bc87cSnicm mtd->menucb(mtd->modedata, mtm->c, key); 1002f43bc87cSnicm 1003f43bc87cSnicm out: 1004f43bc87cSnicm mode_tree_remove_ref(mtd); 1005f43bc87cSnicm free(mtm); 1006f43bc87cSnicm } 1007f43bc87cSnicm 1008f43bc87cSnicm static void 1009f43bc87cSnicm mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x, 1010f43bc87cSnicm u_int y, int outside) 1011f43bc87cSnicm { 1012f43bc87cSnicm struct mode_tree_item *mti; 1013f43bc87cSnicm struct menu *menu; 10141335341aSnicm const struct menu_item *items; 1015f43bc87cSnicm struct mode_tree_menu *mtm; 1016f43bc87cSnicm char *title; 1017f43bc87cSnicm u_int line; 1018f43bc87cSnicm 1019f43bc87cSnicm if (mtd->offset + y > mtd->line_size - 1) 1020f43bc87cSnicm line = mtd->current; 1021f43bc87cSnicm else 1022f43bc87cSnicm line = mtd->offset + y; 1023f43bc87cSnicm mti = mtd->line_list[line].item; 1024f43bc87cSnicm 1025f43bc87cSnicm if (!outside) { 10261335341aSnicm items = mtd->menu; 1027f43bc87cSnicm xasprintf(&title, "#[align=centre]%s", mti->name); 1028f43bc87cSnicm } else { 10291335341aSnicm items = mode_tree_menu_items; 1030f43bc87cSnicm title = xstrdup(""); 1031f43bc87cSnicm } 10321335341aSnicm menu = menu_create(title); 10336a9342afSnicm menu_add_items(menu, items, NULL, c, NULL); 1034f43bc87cSnicm free(title); 1035f43bc87cSnicm 1036f43bc87cSnicm mtm = xmalloc(sizeof *mtm); 1037f43bc87cSnicm mtm->data = mtd; 1038f43bc87cSnicm mtm->c = c; 1039f43bc87cSnicm mtm->line = line; 1040f43bc87cSnicm mtd->references++; 1041f43bc87cSnicm 1042e38aac43Snicm if (x >= (menu->width + 4) / 2) 1043e38aac43Snicm x -= (menu->width + 4) / 2; 1044e38aac43Snicm else 1045e38aac43Snicm x = 0; 104617d7ce67Snicm if (menu_display(menu, 0, 0, NULL, x, y, c, BOX_LINES_DEFAULT, NULL, 1047*be3910bdSnicm NULL, NULL, NULL, mode_tree_menu_callback, mtm) != 0) { 1048*be3910bdSnicm mode_tree_remove_ref(mtd); 1049*be3910bdSnicm free(mtm); 1050f43bc87cSnicm menu_free(menu); 1051f43bc87cSnicm } 1052*be3910bdSnicm } 1053f43bc87cSnicm 1054a42faf7dSnicm int 1055943a08b1Snicm mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, 105663c9949dSnicm struct mouse_event *m, u_int *xp, u_int *yp) 1057a42faf7dSnicm { 1058a42faf7dSnicm struct mode_tree_line *line; 10598874597dSnicm struct mode_tree_item *current, *parent, *mti; 1060a42faf7dSnicm u_int i, x, y; 1061a42faf7dSnicm int choice; 1062a42faf7dSnicm 1063f43bc87cSnicm if (KEYC_IS_MOUSE(*key) && m != NULL) { 1064a42faf7dSnicm if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) { 1065a42faf7dSnicm *key = KEYC_NONE; 1066a42faf7dSnicm return (0); 1067a42faf7dSnicm } 106863c9949dSnicm if (xp != NULL) 106963c9949dSnicm *xp = x; 107063c9949dSnicm if (yp != NULL) 107163c9949dSnicm *yp = y; 1072a42faf7dSnicm if (x > mtd->width || y > mtd->height) { 1073f43bc87cSnicm if (*key == KEYC_MOUSEDOWN3_PANE) 1074f43bc87cSnicm mode_tree_display_menu(mtd, c, x, y, 1); 107575c5b1bcSnicm if (mtd->preview == MODE_TREE_PREVIEW_OFF) 1076a42faf7dSnicm *key = KEYC_NONE; 1077a42faf7dSnicm return (0); 1078a42faf7dSnicm } 1079a42faf7dSnicm if (mtd->offset + y < mtd->line_size) { 1080f4ca57d3Snicm if (*key == KEYC_MOUSEDOWN1_PANE || 1081f43bc87cSnicm *key == KEYC_MOUSEDOWN3_PANE || 1082f4ca57d3Snicm *key == KEYC_DOUBLECLICK1_PANE) 1083a42faf7dSnicm mtd->current = mtd->offset + y; 1084f4ca57d3Snicm if (*key == KEYC_DOUBLECLICK1_PANE) 1085a42faf7dSnicm *key = '\r'; 1086f43bc87cSnicm else { 1087f43bc87cSnicm if (*key == KEYC_MOUSEDOWN3_PANE) 1088f43bc87cSnicm mode_tree_display_menu(mtd, c, x, y, 0); 1089c0c7c81eSnicm *key = KEYC_NONE; 1090f43bc87cSnicm } 1091f43bc87cSnicm } else { 1092f43bc87cSnicm if (*key == KEYC_MOUSEDOWN3_PANE) 1093f43bc87cSnicm mode_tree_display_menu(mtd, c, x, y, 0); 1094c0c7c81eSnicm *key = KEYC_NONE; 1095f43bc87cSnicm } 1096a42faf7dSnicm return (0); 1097a42faf7dSnicm } 1098a42faf7dSnicm 1099a42faf7dSnicm line = &mtd->line_list[mtd->current]; 1100a42faf7dSnicm current = line->item; 1101a42faf7dSnicm 1102a42faf7dSnicm choice = -1; 1103438eed14Snicm for (i = 0; i < mtd->line_size; i++) { 1104438eed14Snicm if (*key == mtd->line_list[i].item->key) { 1105438eed14Snicm choice = i; 1106438eed14Snicm break; 1107438eed14Snicm } 1108a42faf7dSnicm } 1109a42faf7dSnicm if (choice != -1) { 1110a42faf7dSnicm if ((u_int)choice > mtd->line_size - 1) { 1111a42faf7dSnicm *key = KEYC_NONE; 1112a42faf7dSnicm return (0); 1113a42faf7dSnicm } 1114a42faf7dSnicm mtd->current = choice; 1115a42faf7dSnicm *key = '\r'; 1116a42faf7dSnicm return (0); 1117a42faf7dSnicm } 1118a42faf7dSnicm 1119a42faf7dSnicm switch (*key) { 1120a42faf7dSnicm case 'q': 1121a42faf7dSnicm case '\033': /* Escape */ 1122719f5715Snicm case 'g'|KEYC_CTRL: 1123a42faf7dSnicm return (1); 1124a42faf7dSnicm case KEYC_UP: 1125a42faf7dSnicm case 'k': 1126a42faf7dSnicm case KEYC_WHEELUP_PANE: 1127719f5715Snicm case 'p'|KEYC_CTRL: 1128a42faf7dSnicm mode_tree_up(mtd, 1); 1129a42faf7dSnicm break; 1130a42faf7dSnicm case KEYC_DOWN: 1131a42faf7dSnicm case 'j': 1132a42faf7dSnicm case KEYC_WHEELDOWN_PANE: 1133719f5715Snicm case 'n'|KEYC_CTRL: 1134a42faf7dSnicm mode_tree_down(mtd, 1); 1135a42faf7dSnicm break; 1136a42faf7dSnicm case KEYC_PPAGE: 1137719f5715Snicm case 'b'|KEYC_CTRL: 1138a42faf7dSnicm for (i = 0; i < mtd->height; i++) { 1139a42faf7dSnicm if (mtd->current == 0) 1140a42faf7dSnicm break; 1141a42faf7dSnicm mode_tree_up(mtd, 1); 1142a42faf7dSnicm } 1143a42faf7dSnicm break; 1144a42faf7dSnicm case KEYC_NPAGE: 1145719f5715Snicm case 'f'|KEYC_CTRL: 1146a42faf7dSnicm for (i = 0; i < mtd->height; i++) { 1147a42faf7dSnicm if (mtd->current == mtd->line_size - 1) 1148a42faf7dSnicm break; 1149a42faf7dSnicm mode_tree_down(mtd, 1); 1150a42faf7dSnicm } 1151a42faf7dSnicm break; 1152d4840499Snicm case 'g': 1153a42faf7dSnicm case KEYC_HOME: 1154a42faf7dSnicm mtd->current = 0; 1155a42faf7dSnicm mtd->offset = 0; 1156a42faf7dSnicm break; 1157d4840499Snicm case 'G': 1158a42faf7dSnicm case KEYC_END: 1159a42faf7dSnicm mtd->current = mtd->line_size - 1; 1160a42faf7dSnicm if (mtd->current > mtd->height - 1) 11617e53ccf1Snicm mtd->offset = mtd->current - mtd->height + 1; 1162a42faf7dSnicm else 1163a42faf7dSnicm mtd->offset = 0; 1164a42faf7dSnicm break; 1165a42faf7dSnicm case 't': 1166a42faf7dSnicm /* 1167a42faf7dSnicm * Do not allow parents and children to both be tagged: untag 1168a42faf7dSnicm * all parents and children of current. 1169a42faf7dSnicm */ 117094c0d63cSnicm if (current->no_tag) 117194c0d63cSnicm break; 1172a42faf7dSnicm if (!current->tagged) { 1173a42faf7dSnicm parent = current->parent; 1174a42faf7dSnicm while (parent != NULL) { 1175a42faf7dSnicm parent->tagged = 0; 1176a42faf7dSnicm parent = parent->parent; 1177a42faf7dSnicm } 1178a42faf7dSnicm mode_tree_clear_tagged(¤t->children); 1179a42faf7dSnicm current->tagged = 1; 1180a42faf7dSnicm } else 1181a42faf7dSnicm current->tagged = 0; 1182f43bc87cSnicm if (m != NULL) 1183a42faf7dSnicm mode_tree_down(mtd, 0); 1184a42faf7dSnicm break; 1185a42faf7dSnicm case 'T': 1186a42faf7dSnicm for (i = 0; i < mtd->line_size; i++) 1187a42faf7dSnicm mtd->line_list[i].item->tagged = 0; 1188a42faf7dSnicm break; 1189719f5715Snicm case 't'|KEYC_CTRL: 1190a42faf7dSnicm for (i = 0; i < mtd->line_size; i++) { 119194c0d63cSnicm if ((mtd->line_list[i].item->parent == NULL && 119294c0d63cSnicm !mtd->line_list[i].item->no_tag) || 119394c0d63cSnicm (mtd->line_list[i].item->parent != NULL && 119494c0d63cSnicm mtd->line_list[i].item->parent->no_tag)) 1195a42faf7dSnicm mtd->line_list[i].item->tagged = 1; 1196a42faf7dSnicm else 1197a42faf7dSnicm mtd->line_list[i].item->tagged = 0; 1198a42faf7dSnicm } 1199a42faf7dSnicm break; 1200a42faf7dSnicm case 'O': 12013f6f9f7dSnicm mtd->sort_crit.field++; 120267c16a7cSnicm if (mtd->sort_crit.field >= mtd->sort_size) 12033f6f9f7dSnicm mtd->sort_crit.field = 0; 12043f6f9f7dSnicm mode_tree_build(mtd); 12053f6f9f7dSnicm break; 12063f6f9f7dSnicm case 'r': 12073f6f9f7dSnicm mtd->sort_crit.reversed = !mtd->sort_crit.reversed; 1208a42faf7dSnicm mode_tree_build(mtd); 1209a42faf7dSnicm break; 1210a42faf7dSnicm case KEYC_LEFT: 121176d9bd90Snicm case 'h': 1212a42faf7dSnicm case '-': 1213a42faf7dSnicm if (line->flat || !current->expanded) 1214a42faf7dSnicm current = current->parent; 1215a42faf7dSnicm if (current == NULL) 1216a42faf7dSnicm mode_tree_up(mtd, 0); 1217a42faf7dSnicm else { 1218a42faf7dSnicm current->expanded = 0; 1219a42faf7dSnicm mtd->current = current->line; 1220a42faf7dSnicm mode_tree_build(mtd); 1221a42faf7dSnicm } 1222a42faf7dSnicm break; 1223a42faf7dSnicm case KEYC_RIGHT: 122476d9bd90Snicm case 'l': 1225a42faf7dSnicm case '+': 1226a42faf7dSnicm if (line->flat || current->expanded) 1227a42faf7dSnicm mode_tree_down(mtd, 0); 1228a42faf7dSnicm else if (!line->flat) { 1229a42faf7dSnicm current->expanded = 1; 1230a42faf7dSnicm mode_tree_build(mtd); 1231a42faf7dSnicm } 1232a42faf7dSnicm break; 12336a385b80Snicm case '-'|KEYC_META: 12348874597dSnicm TAILQ_FOREACH(mti, &mtd->children, entry) 12358874597dSnicm mti->expanded = 0; 12368874597dSnicm mode_tree_build(mtd); 12378874597dSnicm break; 12386a385b80Snicm case '+'|KEYC_META: 12398874597dSnicm TAILQ_FOREACH(mti, &mtd->children, entry) 12408874597dSnicm mti->expanded = 1; 12418874597dSnicm mode_tree_build(mtd); 12428874597dSnicm break; 1243c186f099Snicm case '?': 1244c186f099Snicm case '/': 1245719f5715Snicm case 's'|KEYC_CTRL: 1246943a08b1Snicm mtd->references++; 124794adf770Snicm status_prompt_set(c, NULL, "(search) ", "", 1248943a08b1Snicm mode_tree_search_callback, mode_tree_search_free, mtd, 1249bc5a8fc2Snicm PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH); 1250943a08b1Snicm break; 1251943a08b1Snicm case 'n': 1252cea02a81Snicm mtd->search_dir = MODE_TREE_SEARCH_FORWARD; 1253cea02a81Snicm mode_tree_search_set(mtd); 1254cea02a81Snicm break; 1255cea02a81Snicm case 'N': 1256cea02a81Snicm mtd->search_dir = MODE_TREE_SEARCH_BACKWARD; 1257943a08b1Snicm mode_tree_search_set(mtd); 1258943a08b1Snicm break; 1259024c311aSnicm case 'f': 1260024c311aSnicm mtd->references++; 126194adf770Snicm status_prompt_set(c, NULL, "(filter) ", mtd->filter, 1262024c311aSnicm mode_tree_filter_callback, mode_tree_filter_free, mtd, 1263bc5a8fc2Snicm PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH); 1264024c311aSnicm break; 1265f84988b9Snicm case 'v': 126675c5b1bcSnicm switch (mtd->preview) { 126775c5b1bcSnicm case MODE_TREE_PREVIEW_OFF: 126875c5b1bcSnicm mtd->preview = MODE_TREE_PREVIEW_BIG; 126975c5b1bcSnicm break; 127075c5b1bcSnicm case MODE_TREE_PREVIEW_NORMAL: 127175c5b1bcSnicm mtd->preview = MODE_TREE_PREVIEW_OFF; 127275c5b1bcSnicm break; 127375c5b1bcSnicm case MODE_TREE_PREVIEW_BIG: 127475c5b1bcSnicm mtd->preview = MODE_TREE_PREVIEW_NORMAL; 127575c5b1bcSnicm break; 127675c5b1bcSnicm } 1277f84988b9Snicm mode_tree_build(mtd); 127875c5b1bcSnicm if (mtd->preview != MODE_TREE_PREVIEW_OFF) 12797e53ccf1Snicm mode_tree_check_selected(mtd); 1280f84988b9Snicm break; 1281a42faf7dSnicm } 1282a42faf7dSnicm return (0); 1283a42faf7dSnicm } 1284a42faf7dSnicm 1285a42faf7dSnicm void 1286a42faf7dSnicm mode_tree_run_command(struct client *c, struct cmd_find_state *fs, 1287a42faf7dSnicm const char *template, const char *name) 1288a42faf7dSnicm { 12891c43462cSnicm struct cmdq_state *state; 12901c43462cSnicm char *command, *error; 12911c43462cSnicm enum cmd_parse_status status; 1292a42faf7dSnicm 1293a42faf7dSnicm command = cmd_template_replace(template, name, 1); 12941c43462cSnicm if (command != NULL && *command != '\0') { 12951c43462cSnicm state = cmdq_new_state(fs, NULL, 0); 12961c43462cSnicm status = cmd_parse_and_append(command, NULL, c, state, &error); 12971c43462cSnicm if (status == CMD_PARSE_ERROR) { 1298df6ab229Snicm if (c != NULL) { 12991c43462cSnicm *error = toupper((u_char)*error); 1300e7e79d0aSnicm status_message_set(c, -1, 1, 0, "%s", error); 1301a42faf7dSnicm } 13021c43462cSnicm free(error); 1303a42faf7dSnicm } 13041c43462cSnicm cmdq_free_state(state); 13051c43462cSnicm } 1306a42faf7dSnicm free(command); 1307a42faf7dSnicm } 1308