tmux  3.2a
About: tmux is a terminal multiplexer that lets you switch easily between several programs in one terminal.
  Fossies Dox: tmux-3.2a.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

mode-tree.c
Go to the documentation of this file.
1 /* $OpenBSD$ */
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 
32  int dead;
33  u_int references;
34  int zoomed;
35 
36  struct window_pane *wp;
37  void *modedata;
38  const struct menu_item *menu;
39 
40  const char **sort_list;
41  u_int sort_size;
43 
50 
51  struct mode_tree_list children;
52  struct mode_tree_list saved;
53 
55  u_int line_size;
56 
57  u_int depth;
58 
59  u_int width;
60  u_int height;
61 
62  u_int offset;
63  u_int current;
64 
65  struct screen screen;
66 
67  int preview;
68  char *search;
69  char *filter;
71 };
72 
75  void *itemdata;
76  u_int line;
77 
79  const char *keystr;
80  size_t keylen;
81 
82  uint64_t tag;
83  const char *name;
84  const char *text;
85 
86  int expanded;
87  int tagged;
88 
90  int no_tag;
91 
92  struct mode_tree_list children;
93  TAILQ_ENTRY(mode_tree_item) entry;
94 };
95 
98  u_int depth;
99  int last;
100  int flat;
101 };
102 
105  struct client *c;
106  u_int line;
107  void *itemdata;
108 };
109 
110 static void mode_tree_free_items(struct mode_tree_list *);
111 
112 static const struct menu_item mode_tree_menu_items[] = {
113  { "Scroll Left", '<', NULL },
114  { "Scroll Right", '>', NULL },
115  { "", KEYC_NONE, NULL },
116  { "Cancel", 'q', NULL },
117 
118  { NULL, KEYC_NONE, NULL }
119 };
120 
121 static struct mode_tree_item *
122 mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
123 {
124  struct mode_tree_item *mti, *child;
125 
126  TAILQ_FOREACH(mti, mtl, entry) {
127  if (mti->tag == tag)
128  return (mti);
129  child = mode_tree_find_item(&mti->children, tag);
130  if (child != NULL)
131  return (child);
132  }
133  return (NULL);
134 }
135 
136 static void
138 {
140 
141  free((void *)mti->name);
142  free((void *)mti->text);
143  free((void *)mti->keystr);
144 
145  free(mti);
146 }
147 
148 static void
149 mode_tree_free_items(struct mode_tree_list *mtl)
150 {
151  struct mode_tree_item *mti, *mti1;
152 
153  TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) {
154  TAILQ_REMOVE(mtl, mti, entry);
155  mode_tree_free_item(mti);
156  }
157 }
158 
159 static void
161 {
162  /*
163  * If the current line would now be off screen reset the offset to the
164  * last visible line.
165  */
166  if (mtd->current > mtd->height - 1)
167  mtd->offset = mtd->current - mtd->height + 1;
168 }
169 
170 static void
172 {
173  free(mtd->line_list);
174  mtd->line_list = NULL;
175  mtd->line_size = 0;
176 }
177 
178 static void
180  struct mode_tree_list *mtl, u_int depth)
181 {
182  struct mode_tree_item *mti;
183  struct mode_tree_line *line;
184  u_int i;
185  int flat = 1;
186 
187  mtd->depth = depth;
188  TAILQ_FOREACH(mti, mtl, entry) {
189  mtd->line_list = xreallocarray(mtd->line_list,
190  mtd->line_size + 1, sizeof *mtd->line_list);
191 
192  line = &mtd->line_list[mtd->line_size++];
193  line->item = mti;
194  line->depth = depth;
195  line->last = (mti == TAILQ_LAST(mtl, mode_tree_list));
196 
197  mti->line = (mtd->line_size - 1);
198  if (!TAILQ_EMPTY(&mti->children))
199  flat = 0;
200  if (mti->expanded)
201  mode_tree_build_lines(mtd, &mti->children, depth + 1);
202 
203  if (mtd->keycb != NULL) {
204  mti->key = mtd->keycb(mtd->modedata, mti->itemdata,
205  mti->line);
206  if (mti->key == KEYC_UNKNOWN)
207  mti->key = KEYC_NONE;
208  } else if (mti->line < 10)
209  mti->key = '0' + mti->line;
210  else if (mti->line < 36)
211  mti->key = KEYC_META|('a' + mti->line - 10);
212  else
213  mti->key = KEYC_NONE;
214  if (mti->key != KEYC_NONE) {
216  0));
217  mti->keylen = strlen(mti->keystr);
218  } else {
219  mti->keystr = NULL;
220  mti->keylen = 0;
221  }
222  }
223  TAILQ_FOREACH(mti, mtl, entry) {
224  for (i = 0; i < mtd->line_size; i++) {
225  line = &mtd->line_list[i];
226  if (line->item == mti)
227  line->flat = flat;
228  }
229  }
230 }
231 
232 static void
233 mode_tree_clear_tagged(struct mode_tree_list *mtl)
234 {
235  struct mode_tree_item *mti;
236 
237  TAILQ_FOREACH(mti, mtl, entry) {
238  mti->tagged = 0;
240  }
241 }
242 
243 void
244 mode_tree_up(struct mode_tree_data *mtd, int wrap)
245 {
246  if (mtd->current == 0) {
247  if (wrap) {
248  mtd->current = mtd->line_size - 1;
249  if (mtd->line_size >= mtd->height)
250  mtd->offset = mtd->line_size - mtd->height;
251  }
252  } else {
253  mtd->current--;
254  if (mtd->current < mtd->offset)
255  mtd->offset--;
256  }
257 }
258 
259 void
260 mode_tree_down(struct mode_tree_data *mtd, int wrap)
261 {
262  if (mtd->current == mtd->line_size - 1) {
263  if (wrap) {
264  mtd->current = 0;
265  mtd->offset = 0;
266  }
267  } else {
268  mtd->current++;
269  if (mtd->current > mtd->offset + mtd->height - 1)
270  mtd->offset++;
271  }
272 }
273 
274 void *
276 {
277  return (mtd->line_list[mtd->current].item->itemdata);
278 }
279 
280 const char *
282 {
283  return (mtd->line_list[mtd->current].item->name);
284 }
285 
286 void
288 {
289  if (!mtd->line_list[mtd->current].item->expanded) {
290  mtd->line_list[mtd->current].item->expanded = 1;
291  mode_tree_build(mtd);
292  }
293 }
294 
295 void
297 {
298  if (mtd->line_list[mtd->current].item->expanded) {
299  mtd->line_list[mtd->current].item->expanded = 0;
300  mode_tree_build(mtd);
301  }
302 }
303 
304 static int
305 mode_tree_get_tag(struct mode_tree_data *mtd, uint64_t tag, u_int *found)
306 {
307  u_int i;
308 
309  for (i = 0; i < mtd->line_size; i++) {
310  if (mtd->line_list[i].item->tag == tag)
311  break;
312  }
313  if (i != mtd->line_size) {
314  *found = i;
315  return (1);
316  }
317  return (0);
318 }
319 
320 void
321 mode_tree_expand(struct mode_tree_data *mtd, uint64_t tag)
322 {
323  u_int found;
324 
325  if (!mode_tree_get_tag(mtd, tag, &found))
326  return;
327  if (!mtd->line_list[found].item->expanded) {
328  mtd->line_list[found].item->expanded = 1;
329  mode_tree_build(mtd);
330  }
331 }
332 
333 int
335 {
336  u_int found;
337 
338  if (mode_tree_get_tag(mtd, tag, &found)) {
339  mtd->current = found;
340  if (mtd->current > mtd->height - 1)
341  mtd->offset = mtd->current - mtd->height + 1;
342  else
343  mtd->offset = 0;
344  return (1);
345  }
346  mtd->current = 0;
347  mtd->offset = 0;
348  return (0);
349 }
350 
351 u_int
353 {
354  struct mode_tree_item *mti;
355  u_int i, tagged;
356 
357  tagged = 0;
358  for (i = 0; i < mtd->line_size; i++) {
359  mti = mtd->line_list[i].item;
360  if (mti->tagged)
361  tagged++;
362  }
363  return (tagged);
364 }
365 
366 void
368  struct client *c, key_code key, int current)
369 {
370  struct mode_tree_item *mti;
371  u_int i;
372  int fired;
373 
374  fired = 0;
375  for (i = 0; i < mtd->line_size; i++) {
376  mti = mtd->line_list[i].item;
377  if (mti->tagged) {
378  fired = 1;
379  cb(mtd->modedata, mti->itemdata, c, key);
380  }
381  }
382  if (!fired && current) {
383  mti = mtd->line_list[mtd->current].item;
384  cb(mtd->modedata, mti->itemdata, c, key);
385  }
386 }
387 
388 struct mode_tree_data *
393  const struct menu_item *menu, const char **sort_list, u_int sort_size,
394  struct screen **s)
395 {
396  struct mode_tree_data *mtd;
397  const char *sort;
398  u_int i;
399 
400  mtd = xcalloc(1, sizeof *mtd);
401  mtd->references = 1;
402 
403  mtd->wp = wp;
404  mtd->modedata = modedata;
405  mtd->menu = menu;
406 
407  mtd->sort_list = sort_list;
408  mtd->sort_size = sort_size;
409 
410  mtd->preview = !args_has(args, 'N');
411 
412  sort = args_get(args, 'O');
413  if (sort != NULL) {
414  for (i = 0; i < sort_size; i++) {
415  if (strcasecmp(sort, sort_list[i]) == 0)
416  mtd->sort_crit.field = i;
417  }
418  }
419  mtd->sort_crit.reversed = args_has(args, 'r');
420 
421  if (args_has(args, 'f'))
422  mtd->filter = xstrdup(args_get(args, 'f'));
423  else
424  mtd->filter = NULL;
425 
426  mtd->buildcb = buildcb;
427  mtd->drawcb = drawcb;
428  mtd->searchcb = searchcb;
429  mtd->menucb = menucb;
430  mtd->heightcb = heightcb;
431  mtd->keycb = keycb;
432 
433  TAILQ_INIT(&mtd->children);
434 
435  *s = &mtd->screen;
437  (*s)->mode &= ~~MODE_CURSOR;
438 
439  return (mtd);
440 }
441 
442 void
443 mode_tree_zoom(struct mode_tree_data *mtd, struct args *args)
444 {
445  struct window_pane *wp = mtd->wp;
446 
447  if (args_has(args, 'Z')) {
448  mtd->zoomed = (wp->window->flags & WINDOW_ZOOMED);
449  if (!mtd->zoomed && window_zoom(wp) == 0)
451  } else
452  mtd->zoomed = -1;
453 }
454 
455 static void
457 {
458  struct screen *s = &mtd->screen;
459  u_int height;
460 
461  if (mtd->heightcb != NULL) {
462  height = mtd->heightcb(mtd, screen_size_y(s));
463  if (height < screen_size_y(s))
464  mtd->height = screen_size_y(s) - height;
465  } else {
466  mtd->height = (screen_size_y(s) / 3) * 2;
467  if (mtd->height > mtd->line_size)
468  mtd->height = screen_size_y(s) / 2;
469  }
470  if (mtd->height < 10)
471  mtd->height = screen_size_y(s);
472  if (screen_size_y(s) - mtd->height < 2)
473  mtd->height = screen_size_y(s);
474 }
475 
476 void
478 {
479  struct screen *s = &mtd->screen;
480  uint64_t tag;
481 
482  if (mtd->line_list != NULL)
483  tag = mtd->line_list[mtd->current].item->tag;
484  else
485  tag = UINT64_MAX;
486 
487  TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
488  TAILQ_INIT(&mtd->children);
489 
490  mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter);
491  mtd->no_matches = TAILQ_EMPTY(&mtd->children);
492  if (mtd->no_matches)
493  mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, NULL);
494 
496  TAILQ_INIT(&mtd->saved);
497 
499  mode_tree_build_lines(mtd, &mtd->children, 0);
500 
501  if (tag == UINT64_MAX)
502  tag = mtd->line_list[mtd->current].item->tag;
503  mode_tree_set_current(mtd, tag);
504 
505  mtd->width = screen_size_x(s);
506  if (mtd->preview)
508  else
509  mtd->height = screen_size_y(s);
511 }
512 
513 static void
515 {
516  if (--mtd->references == 0)
517  free(mtd);
518 }
519 
520 void
522 {
523  struct window_pane *wp = mtd->wp;
524 
525  if (mtd->zoomed == 0)
527 
530  screen_free(&mtd->screen);
531 
532  free(mtd->search);
533  free(mtd->filter);
534 
535  mtd->dead = 1;
537 }
538 
539 void
540 mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
541 {
542  struct screen *s = &mtd->screen;
543 
544  screen_resize(s, sx, sy, 0);
545 
546  mode_tree_build(mtd);
547  mode_tree_draw(mtd);
548 
549  mtd->wp->flags |= PANE_REDRAW;
550 }
551 
552 struct mode_tree_item *
554  void *itemdata, uint64_t tag, const char *name, const char *text,
555  int expanded)
556 {
557  struct mode_tree_item *mti, *saved;
558 
559  log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
560  name, (text == NULL ? "" : text));
561 
562  mti = xcalloc(1, sizeof *mti);
563  mti->parent = parent;
564  mti->itemdata = itemdata;
565 
566  mti->tag = tag;
567  mti->name = xstrdup(name);
568  if (text != NULL)
569  mti->text = xstrdup(text);
570 
571  saved = mode_tree_find_item(&mtd->saved, tag);
572  if (saved != NULL) {
573  if (parent == NULL || parent->expanded)
574  mti->tagged = saved->tagged;
575  mti->expanded = saved->expanded;
576  } else if (expanded == -1)
577  mti->expanded = 1;
578  else
579  mti->expanded = expanded;
580 
581  TAILQ_INIT(&mti->children);
582 
583  if (parent != NULL)
584  TAILQ_INSERT_TAIL(&parent->children, mti, entry);
585  else
586  TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
587 
588  return (mti);
589 }
590 
591 void
593 {
594  mti->draw_as_parent = 1;
595 }
596 
597 void
599 {
600  mti->no_tag = 1;
601 }
602 
603 void
605 {
606  struct mode_tree_item *parent = mti->parent;
607 
608  if (parent != NULL)
609  TAILQ_REMOVE(&parent->children, mti, entry);
610  else
611  TAILQ_REMOVE(&mtd->children, mti, entry);
612  mode_tree_free_item(mti);
613 }
614 
615 void
617 {
618  struct window_pane *wp = mtd->wp;
619  struct screen *s = &mtd->screen;
620  struct mode_tree_line *line;
621  struct mode_tree_item *mti;
622  struct options *oo = wp->window->options;
623  struct screen_write_ctx ctx;
624  struct grid_cell gc0, gc;
625  u_int w, h, i, j, sy, box_x, box_y, width;
626  char *text, *start, *key;
627  const char *tag, *symbol;
628  size_t size, n;
629  int keylen, pad;
630 
631  if (mtd->line_size == 0)
632  return;
633 
634  memcpy(&gc0, &grid_default_cell, sizeof gc0);
635  memcpy(&gc, &grid_default_cell, sizeof gc);
636  style_apply(&gc, oo, "mode-style", NULL);
637 
638  w = mtd->width;
639  h = mtd->height;
640 
641  screen_write_start(&ctx, s);
642  screen_write_clearscreen(&ctx, 8);
643 
644  keylen = 0;
645  for (i = 0; i < mtd->line_size; i++) {
646  mti = mtd->line_list[i].item;
647  if (mti->key == KEYC_NONE)
648  continue;
649  if ((int)mti->keylen + 3 > keylen)
650  keylen = mti->keylen + 3;
651  }
652 
653  for (i = 0; i < mtd->line_size; i++) {
654  if (i < mtd->offset)
655  continue;
656  if (i > mtd->offset + h - 1)
657  break;
658  line = &mtd->line_list[i];
659  mti = line->item;
660 
661  screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
662 
663  pad = keylen - 2 - mti->keylen;
664  if (mti->key != KEYC_NONE)
665  xasprintf(&key, "(%s)%*s", mti->keystr, pad, "");
666  else
667  key = xstrdup("");
668 
669  if (line->flat)
670  symbol = "";
671  else if (TAILQ_EMPTY(&mti->children))
672  symbol = " ";
673  else if (mti->expanded)
674  symbol = "- ";
675  else
676  symbol = "+ ";
677 
678  if (line->depth == 0)
679  start = xstrdup(symbol);
680  else {
681  size = (4 * line->depth) + 32;
682 
683  start = xcalloc(1, size);
684  for (j = 1; j < line->depth; j++) {
685  if (mti->parent != NULL &&
686  mtd->line_list[mti->parent->line].last)
687  strlcat(start, " ", size);
688  else
689  strlcat(start, "\001x\001 ", size);
690  }
691  if (line->last)
692  strlcat(start, "\001mq\001> ", size);
693  else
694  strlcat(start, "\001tq\001> ", size);
695  strlcat(start, symbol, size);
696  }
697 
698  if (mti->tagged)
699  tag = "*";
700  else
701  tag = "";
702  xasprintf(&text, "%-*s%s%s%s%s", keylen, key, start, mti->name,
703  tag, (mti->text != NULL) ? ": " : "" );
704  width = utf8_cstrwidth(text);
705  if (width > w)
706  width = w;
707  free(start);
708 
709  if (mti->tagged) {
710  gc.attr ^= GRID_ATTR_BRIGHT;
711  gc0.attr ^= GRID_ATTR_BRIGHT;
712  }
713 
714  if (i != mtd->current) {
716  screen_write_nputs(&ctx, w, &gc0, "%s", text);
717  if (mti->text != NULL) {
718  format_draw(&ctx, &gc0, w - width, mti->text,
719  NULL);
720  }
721  } else {
723  screen_write_nputs(&ctx, w, &gc, "%s", text);
724  if (mti->text != NULL) {
725  format_draw(&ctx, &gc, w - width, mti->text,
726  NULL);
727  }
728  }
729  free(text);
730  free(key);
731 
732  if (mti->tagged) {
733  gc.attr ^= GRID_ATTR_BRIGHT;
734  gc0.attr ^= GRID_ATTR_BRIGHT;
735  }
736  }
737 
738  sy = screen_size_y(s);
739  if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
740  screen_write_stop(&ctx);
741  return;
742  }
743 
744  line = &mtd->line_list[mtd->current];
745  mti = line->item;
746  if (mti->draw_as_parent)
747  mti = mti->parent;
748 
749  screen_write_cursormove(&ctx, 0, h, 0);
750  screen_write_box(&ctx, w, sy - h);
751 
752  if (mtd->sort_list != NULL) {
753  xasprintf(&text, " %s (sort: %s%s)", mti->name,
754  mtd->sort_list[mtd->sort_crit.field],
755  mtd->sort_crit.reversed ? ", reversed" : "");
756  } else
757  xasprintf(&text, " %s", mti->name);
758  if (w - 2 >= strlen(text)) {
759  screen_write_cursormove(&ctx, 1, h, 0);
760  screen_write_puts(&ctx, &gc0, "%s", text);
761 
762  if (mtd->no_matches)
763  n = (sizeof "no matches") - 1;
764  else
765  n = (sizeof "active") - 1;
766  if (mtd->filter != NULL && w - 2 >= strlen(text) + 10 + n + 2) {
767  screen_write_puts(&ctx, &gc0, " (filter: ");
768  if (mtd->no_matches)
769  screen_write_puts(&ctx, &gc, "no matches");
770  else
771  screen_write_puts(&ctx, &gc0, "active");
772  screen_write_puts(&ctx, &gc0, ") ");
773  } else
774  screen_write_puts(&ctx, &gc0, " ");
775  }
776  free(text);
777 
778  box_x = w - 4;
779  box_y = sy - h - 2;
780 
781  if (box_x != 0 && box_y != 0) {
782  screen_write_cursormove(&ctx, 2, h + 1, 0);
783  mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y);
784  }
785 
786  screen_write_stop(&ctx);
787 }
788 
789 static struct mode_tree_item *
791 {
792  struct mode_tree_item *mti, *last, *next;
793 
794  if (mtd->search == NULL)
795  return (NULL);
796 
797  mti = last = mtd->line_list[mtd->current].item;
798  for (;;) {
799  if (!TAILQ_EMPTY(&mti->children))
800  mti = TAILQ_FIRST(&mti->children);
801  else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
802  mti = next;
803  else {
804  for (;;) {
805  mti = mti->parent;
806  if (mti == NULL)
807  break;
808  if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
809  mti = next;
810  break;
811  }
812  }
813  }
814  if (mti == NULL)
815  mti = TAILQ_FIRST(&mtd->children);
816  if (mti == last)
817  break;
818 
819  if (mtd->searchcb == NULL) {
820  if (strstr(mti->name, mtd->search) != NULL)
821  return (mti);
822  continue;
823  }
824  if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
825  return (mti);
826  }
827  return (NULL);
828 }
829 
830 static void
832 {
833  struct mode_tree_item *mti, *loop;
834  uint64_t tag;
835 
836  mti = mode_tree_search_for(mtd);
837  if (mti == NULL)
838  return;
839  tag = mti->tag;
840 
841  loop = mti->parent;
842  while (loop != NULL) {
843  loop->expanded = 1;
844  loop = loop->parent;
845  }
846 
847  mode_tree_build(mtd);
849  mode_tree_draw(mtd);
850  mtd->wp->flags |= PANE_REDRAW;
851 }
852 
853 static int
854 mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
855  __unused int done)
856 {
857  struct mode_tree_data *mtd = data;
858 
859  if (mtd->dead)
860  return (0);
861 
862  free(mtd->search);
863  if (s == NULL || *s == '\0') {
864  mtd->search = NULL;
865  return (0);
866  }
867  mtd->search = xstrdup(s);
869 
870  return (0);
871 }
872 
873 static void
875 {
876  mode_tree_remove_ref(data);
877 }
878 
879 static int
880 mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
881  __unused int done)
882 {
883  struct mode_tree_data *mtd = data;
884 
885  if (mtd->dead)
886  return (0);
887 
888  if (mtd->filter != NULL)
889  free(mtd->filter);
890  if (s == NULL || *s == '\0')
891  mtd->filter = NULL;
892  else
893  mtd->filter = xstrdup(s);
894 
895  mode_tree_build(mtd);
896  mode_tree_draw(mtd);
897  mtd->wp->flags |= PANE_REDRAW;
898 
899  return (0);
900 }
901 
902 static void
904 {
905  mode_tree_remove_ref(data);
906 }
907 
908 static void
910  key_code key, void *data)
911 {
912  struct mode_tree_menu *mtm = data;
913  struct mode_tree_data *mtd = mtm->data;
914  struct mode_tree_item *mti;
915 
916  if (mtd->dead || key == KEYC_NONE)
917  goto out;
918 
919  if (mtm->line >= mtd->line_size)
920  goto out;
921  mti = mtd->line_list[mtm->line].item;
922  if (mti->itemdata != mtm->itemdata)
923  goto out;
924  mtd->current = mtm->line;
925  mtd->menucb (mtd->modedata, mtm->c, key);
926 
927 out:
929  free(mtm);
930 }
931 
932 static void
933 mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
934  u_int y, int outside)
935 {
936  struct mode_tree_item *mti;
937  struct menu *menu;
938  const struct menu_item *items;
939  struct mode_tree_menu *mtm;
940  char *title;
941  u_int line;
942 
943  if (mtd->offset + y > mtd->line_size - 1)
944  line = mtd->current;
945  else
946  line = mtd->offset + y;
947  mti = mtd->line_list[line].item;
948 
949  if (!outside) {
950  items = mtd->menu;
951  xasprintf(&title, "#[align=centre]%s", mti->name);
952  } else {
953  items = mode_tree_menu_items;
954  title = xstrdup("");
955  }
956  menu = menu_create(title);
957  menu_add_items(menu, items, NULL, NULL, NULL);
958  free(title);
959 
960  mtm = xmalloc(sizeof *mtm);
961  mtm->data = mtd;
962  mtm->c = c;
963  mtm->line = line;
964  mtm->itemdata = mti->itemdata;
965  mtd->references++;
966 
967  if (x >= (menu->width + 4) / 2)
968  x -= (menu->width + 4) / 2;
969  else
970  x = 0;
971  if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback,
972  mtm) != 0)
973  menu_free(menu);
974 }
975 
976 int
978  struct mouse_event *m, u_int *xp, u_int *yp)
979 {
980  struct mode_tree_line *line;
981  struct mode_tree_item *current, *parent, *mti;
982  u_int i, x, y;
983  int choice;
984 
985  if (KEYC_IS_MOUSE(*key) && m != NULL) {
986  if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
987  *key = KEYC_NONE;
988  return (0);
989  }
990  if (xp != NULL)
991  *xp = x;
992  if (yp != NULL)
993  *yp = y;
994  if (x > mtd->width || y > mtd->height) {
995  if (*key == KEYC_MOUSEDOWN3_PANE)
996  mode_tree_display_menu(mtd, c, x, y, 1);
997  if (!mtd->preview)
998  *key = KEYC_NONE;
999  return (0);
1000  }
1001  if (mtd->offset + y < mtd->line_size) {
1002  if (*key == KEYC_MOUSEDOWN1_PANE ||
1003  *key == KEYC_MOUSEDOWN3_PANE ||
1005  mtd->current = mtd->offset + y;
1006  if (*key == KEYC_DOUBLECLICK1_PANE)
1007  *key = '\r';
1008  else {
1009  if (*key == KEYC_MOUSEDOWN3_PANE)
1010  mode_tree_display_menu(mtd, c, x, y, 0);
1011  *key = KEYC_NONE;
1012  }
1013  } else {
1014  if (*key == KEYC_MOUSEDOWN3_PANE)
1015  mode_tree_display_menu(mtd, c, x, y, 0);
1016  *key = KEYC_NONE;
1017  }
1018  return (0);
1019  }
1020 
1021  line = &mtd->line_list[mtd->current];
1022  current = line->item;
1023 
1024  choice = -1;
1025  for (i = 0; i < mtd->line_size; i++) {
1026  if (*key == mtd->line_list[i].item->key) {
1027  choice = i;
1028  break;
1029  }
1030  }
1031  if (choice != -1) {
1032  if ((u_int)choice > mtd->line_size - 1) {
1033  *key = KEYC_NONE;
1034  return (0);
1035  }
1036  mtd->current = choice;
1037  *key = '\r';
1038  return (0);
1039  }
1040 
1041  switch (*key) {
1042  case 'q':
1043  case '\033': /* Escape */
1044  case '\007': /* C-g */
1045  return (1);
1046  case KEYC_UP:
1047  case 'k':
1048  case KEYC_WHEELUP_PANE:
1049  case '\020': /* C-p */
1050  mode_tree_up(mtd, 1);
1051  break;
1052  case KEYC_DOWN:
1053  case 'j':
1054  case KEYC_WHEELDOWN_PANE:
1055  case '\016': /* C-n */
1056  mode_tree_down(mtd, 1);
1057  break;
1058  case 'g':
1059  case KEYC_PPAGE:
1060  case '\002': /* C-b */
1061  for (i = 0; i < mtd->height; i++) {
1062  if (mtd->current == 0)
1063  break;
1064  mode_tree_up(mtd, 1);
1065  }
1066  break;
1067  case 'G':
1068  case KEYC_NPAGE:
1069  case '\006': /* C-f */
1070  for (i = 0; i < mtd->height; i++) {
1071  if (mtd->current == mtd->line_size - 1)
1072  break;
1073  mode_tree_down(mtd, 1);
1074  }
1075  break;
1076  case KEYC_HOME:
1077  mtd->current = 0;
1078  mtd->offset = 0;
1079  break;
1080  case KEYC_END:
1081  mtd->current = mtd->line_size - 1;
1082  if (mtd->current > mtd->height - 1)
1083  mtd->offset = mtd->current - mtd->height + 1;
1084  else
1085  mtd->offset = 0;
1086  break;
1087  case 't':
1088  /*
1089  * Do not allow parents and children to both be tagged: untag
1090  * all parents and children of current.
1091  */
1092  if (current->no_tag)
1093  break;
1094  if (!current->tagged) {
1095  parent = current->parent;
1096  while (parent != NULL) {
1097  parent->tagged = 0;
1098  parent = parent->parent;
1099  }
1100  mode_tree_clear_tagged(&current->children);
1101  current->tagged = 1;
1102  } else
1103  current->tagged = 0;
1104  if (m != NULL)
1105  mode_tree_down(mtd, 0);
1106  break;
1107  case 'T':
1108  for (i = 0; i < mtd->line_size; i++)
1109  mtd->line_list[i].item->tagged = 0;
1110  break;
1111  case '\024': /* C-t */
1112  for (i = 0; i < mtd->line_size; i++) {
1113  if ((mtd->line_list[i].item->parent == NULL &&
1114  !mtd->line_list[i].item->no_tag) ||
1115  (mtd->line_list[i].item->parent != NULL &&
1116  mtd->line_list[i].item->parent->no_tag))
1117  mtd->line_list[i].item->tagged = 1;
1118  else
1119  mtd->line_list[i].item->tagged = 0;
1120  }
1121  break;
1122  case 'O':
1123  mtd->sort_crit.field++;
1124  if (mtd->sort_crit.field >= mtd->sort_size)
1125  mtd->sort_crit.field = 0;
1126  mode_tree_build(mtd);
1127  break;
1128  case 'r':
1129  mtd->sort_crit.reversed = !mtd->sort_crit.reversed;
1130  mode_tree_build(mtd);
1131  break;
1132  case KEYC_LEFT:
1133  case 'h':
1134  case '-':
1135  if (line->flat || !current->expanded)
1136  current = current->parent;
1137  if (current == NULL)
1138  mode_tree_up(mtd, 0);
1139  else {
1140  current->expanded = 0;
1141  mtd->current = current->line;
1142  mode_tree_build(mtd);
1143  }
1144  break;
1145  case KEYC_RIGHT:
1146  case 'l':
1147  case '+':
1148  if (line->flat || current->expanded)
1149  mode_tree_down(mtd, 0);
1150  else if (!line->flat) {
1151  current->expanded = 1;
1152  mode_tree_build(mtd);
1153  }
1154  break;
1155  case '-'|KEYC_META:
1156  TAILQ_FOREACH(mti, &mtd->children, entry)
1157  mti->expanded = 0;
1158  mode_tree_build(mtd);
1159  break;
1160  case '+'|KEYC_META:
1161  TAILQ_FOREACH(mti, &mtd->children, entry)
1162  mti->expanded = 1;
1163  mode_tree_build(mtd);
1164  break;
1165  case '?':
1166  case '/':
1167  case '\023': /* C-s */
1168  mtd->references++;
1169  status_prompt_set(c, NULL, "(search) ", "",
1171  PROMPT_NOFORMAT);
1172  break;
1173  case 'n':
1174  mode_tree_search_set(mtd);
1175  break;
1176  case 'f':
1177  mtd->references++;
1178  status_prompt_set(c, NULL, "(filter) ", mtd->filter,
1180  PROMPT_NOFORMAT);
1181  break;
1182  case 'v':
1183  mtd->preview = !mtd->preview;
1184  mode_tree_build(mtd);
1185  if (mtd->preview)
1187  break;
1188  }
1189  return (0);
1190 }
1191 
1192 void
1194  const char *template, const char *name)
1195 {
1196  struct cmdq_state *state;
1197  char *command, *error;
1198  enum cmd_parse_status status;
1199 
1200  command = cmd_template_replace(template, name, 1);
1201  if (command != NULL && *command != '\0') {
1202  state = cmdq_new_state(fs, NULL, 0);
1203  status = cmd_parse_and_append(command, NULL, c, state, &error);
1204  if (status == CMD_PARSE_ERROR) {
1205  if (c != NULL) {
1206  *error = toupper((u_char)*error);
1207  status_message_set(c, -1, 1, 0, "%s", error);
1208  }
1209  free(error);
1210  }
1211  cmdq_free_state(state);
1212  }
1213  free(command);
1214 }
int args_has(struct args *args, u_char flag)
Definition: arguments.c:259
const char * args_get(struct args *args, u_char flag)
Definition: arguments.c:295
enum cmd_parse_status cmd_parse_and_append(const char *s, struct cmd_parse_input *pi, struct client *c, struct cmdq_state *state, char **error)
Definition: cmd-parse.c:641
void cmdq_free_state(struct cmdq_state *state)
Definition: cmd-queue.c:244
struct cmdq_state * cmdq_new_state(struct cmd_find_state *current, struct key_event *event, int flags)
Definition: cmd-queue.c:206
int cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, u_int *yp, int last)
Definition: cmd.c:703
char * cmd_template_replace(const char *template, const char *s, int idx)
Definition: cmd.c:778
#define __unused
Definition: compat.h:60
size_t strlcat(char *, const char *, size_t)
void format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, u_int available, const char *expanded, struct style_ranges *srs)
Definition: format-draw.c:649
const struct grid_cell grid_default_cell
Definition: grid.c:39
key_code key
Definition: key-string.c:32
const char * key_string_lookup_key(key_code key, int with_flags)
Definition: key-string.c:265
const char * name
Definition: layout-set.c:38
void log_debug(const char *msg,...)
Definition: log.c:130
void menu_add_items(struct menu *menu, const struct menu_item *items, struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
Definition: menu.c:44
struct menu * menu_create(const char *title)
Definition: menu.c:108
int menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, void *data)
Definition: menu.c:346
void menu_free(struct menu *menu)
Definition: menu.c:120
static int mode_tree_search_callback(struct client *c, void *data, const char *s, int done)
Definition: mode-tree.c:854
static const struct menu_item mode_tree_menu_items[]
Definition: mode-tree.c:112
static int mode_tree_get_tag(struct mode_tree_data *mtd, uint64_t tag, u_int *found)
Definition: mode-tree.c:305
static void mode_tree_free_item(struct mode_tree_item *mti)
Definition: mode-tree.c:137
void mode_tree_down(struct mode_tree_data *mtd, int wrap)
Definition: mode-tree.c:260
void mode_tree_draw_as_parent(struct mode_tree_item *mti)
Definition: mode-tree.c:592
TAILQ_HEAD(mode_tree_list, mode_tree_item)
static void mode_tree_clear_lines(struct mode_tree_data *mtd)
Definition: mode-tree.c:171
static void mode_tree_clear_tagged(struct mode_tree_list *mtl)
Definition: mode-tree.c:233
static int mode_tree_filter_callback(struct client *c, void *data, const char *s, int done)
Definition: mode-tree.c:880
static void mode_tree_search_free(void *data)
Definition: mode-tree.c:874
static void mode_tree_remove_ref(struct mode_tree_data *mtd)
Definition: mode-tree.c:514
void mode_tree_collapse_current(struct mode_tree_data *mtd)
Definition: mode-tree.c:296
void mode_tree_build(struct mode_tree_data *mtd)
Definition: mode-tree.c:477
static void mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x, u_int y, int outside)
Definition: mode-tree.c:933
void mode_tree_free(struct mode_tree_data *mtd)
Definition: mode-tree.c:521
struct mode_tree_data * mode_tree_start(struct window_pane *wp, struct args *args, mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb, mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, mode_tree_height_cb heightcb, mode_tree_key_cb keycb, void *modedata, const struct menu_item *menu, const char **sort_list, u_int sort_size, struct screen **s)
Definition: mode-tree.c:389
const char * mode_tree_get_current_name(struct mode_tree_data *mtd)
Definition: mode-tree.c:281
static void mode_tree_free_items(struct mode_tree_list *)
Definition: mode-tree.c:149
static void mode_tree_menu_callback(struct menu *menu, u_int idx, key_code key, void *data)
Definition: mode-tree.c:909
static void mode_tree_build_lines(struct mode_tree_data *mtd, struct mode_tree_list *mtl, u_int depth)
Definition: mode-tree.c:179
static struct mode_tree_item * mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
Definition: mode-tree.c:122
void mode_tree_expand_current(struct mode_tree_data *mtd)
Definition: mode-tree.c:287
void mode_tree_expand(struct mode_tree_data *mtd, uint64_t tag)
Definition: mode-tree.c:321
void mode_tree_zoom(struct mode_tree_data *mtd, struct args *args)
Definition: mode-tree.c:443
static struct mode_tree_item * mode_tree_search_for(struct mode_tree_data *mtd)
Definition: mode-tree.c:790
u_int mode_tree_count_tagged(struct mode_tree_data *mtd)
Definition: mode-tree.c:352
int mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
Definition: mode-tree.c:334
static void mode_tree_check_selected(struct mode_tree_data *mtd)
Definition: mode-tree.c:160
struct mode_tree_item * mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent, void *itemdata, uint64_t tag, const char *name, const char *text, int expanded)
Definition: mode-tree.c:553
void mode_tree_up(struct mode_tree_data *mtd, int wrap)
Definition: mode-tree.c:244
void mode_tree_run_command(struct client *c, struct cmd_find_state *fs, const char *template, const char *name)
Definition: mode-tree.c:1193
void mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
Definition: mode-tree.c:604
static void mode_tree_search_set(struct mode_tree_data *mtd)
Definition: mode-tree.c:831
int mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, struct mouse_event *m, u_int *xp, u_int *yp)
Definition: mode-tree.c:977
void * mode_tree_get_current(struct mode_tree_data *mtd)
Definition: mode-tree.c:275
static void mode_tree_set_height(struct mode_tree_data *mtd)
Definition: mode-tree.c:456
void mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb, struct client *c, key_code key, int current)
Definition: mode-tree.c:367
void mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
Definition: mode-tree.c:540
void mode_tree_draw(struct mode_tree_data *mtd)
Definition: mode-tree.c:616
void mode_tree_no_tag(struct mode_tree_item *mti)
Definition: mode-tree.c:598
static void mode_tree_filter_free(void *data)
Definition: mode-tree.c:903
void screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
void screen_write_start(struct screen_write_ctx *ctx, struct screen *s)
Definition: screen-write.c:284
void screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp, const char *fmt,...)
Definition: screen-write.c:464
void screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny)
Definition: screen-write.c:672
void screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py, int origin)
void screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, const struct grid_cell *gcp, const char *fmt,...)
Definition: screen-write.c:476
void screen_write_stop(struct screen_write_ctx *ctx)
Definition: screen-write.c:296
void screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
void screen_resize(struct screen *s, u_int sx, u_int sy, int reflow)
Definition: screen.c:276
void screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
Definition: screen.c:74
void screen_free(struct screen *s)
Definition: screen.c:122
void server_redraw_window(struct window *w)
Definition: server-fn.c:94
void server_unzoom_window(struct window *w)
Definition: server-fn.c:486
void status_message_set(struct client *c, int delay, int ignore_styles, int ignore_keys, const char *fmt,...)
Definition: status.c:428
void status_prompt_set(struct client *c, struct cmd_find_state *fs, const char *msg, const char *input, prompt_input_cb inputcb, prompt_free_cb freecb, void *data, int flags)
Definition: status.c:546
Definition: tmux.h:1435
Definition: tmux.h:1608
int bg
Definition: tmux.h:693
u_short attr
Definition: tmux.h:690
Definition: tmux.h:892
u_int width
Definition: tmux.h:896
const struct menu_item * menu
Definition: mode-tree.c:38
u_int line_size
Definition: mode-tree.c:55
struct screen screen
Definition: mode-tree.c:65
u_int references
Definition: mode-tree.c:33
char * search
Definition: mode-tree.c:68
u_int current
Definition: mode-tree.c:63
u_int offset
Definition: mode-tree.c:62
mode_tree_search_cb searchcb
Definition: mode-tree.c:46
u_int sort_size
Definition: mode-tree.c:41
mode_tree_menu_cb menucb
Definition: mode-tree.c:47
u_int height
Definition: mode-tree.c:60
struct mode_tree_sort_criteria sort_crit
Definition: mode-tree.c:42
struct mode_tree_list children
Definition: mode-tree.c:51
mode_tree_height_cb heightcb
Definition: mode-tree.c:48
char * filter
Definition: mode-tree.c:69
struct mode_tree_list saved
Definition: mode-tree.c:52
mode_tree_draw_cb drawcb
Definition: mode-tree.c:45
mode_tree_key_cb keycb
Definition: mode-tree.c:49
void * modedata
Definition: mode-tree.c:37
const char ** sort_list
Definition: mode-tree.c:40
struct window_pane * wp
Definition: mode-tree.c:36
struct mode_tree_line * line_list
Definition: mode-tree.c:54
mode_tree_build_cb buildcb
Definition: mode-tree.c:44
void * itemdata
Definition: mode-tree.c:75
const char * name
Definition: mode-tree.c:83
struct mode_tree_item * parent
Definition: mode-tree.c:74
uint64_t tag
Definition: mode-tree.c:82
struct mode_tree_list children
Definition: mode-tree.c:92
const char * text
Definition: mode-tree.c:84
size_t keylen
Definition: mode-tree.c:80
key_code key
Definition: mode-tree.c:78
int draw_as_parent
Definition: mode-tree.c:89
const char * keystr
Definition: mode-tree.c:79
struct mode_tree_item * item
Definition: mode-tree.c:97
struct mode_tree_data * data
Definition: mode-tree.c:104
void * itemdata
Definition: mode-tree.c:107
struct client * c
Definition: mode-tree.c:105
Definition: tmux.h:816
int flags
Definition: tmux.h:977
struct screen base
Definition: tmux.h:1021
u_int sy
Definition: tmux.h:969
struct window * window
Definition: tmux.h:962
u_int sx
Definition: tmux.h:968
int flags
Definition: tmux.h:1073
struct options * options
Definition: tmux.h:1085
void style_apply(struct grid_cell *gc, struct options *oo, const char *name, struct format_tree *ft)
Definition: style.c:298
#define KEYC_UNKNOWN
Definition: tmux.h:114
#define KEYC_IS_MOUSE(key)
Definition: tmux.h:144
#define WINDOW_ZOOMED
Definition: tmux.h:1077
#define KEYC_META
Definition: tmux.h:124
key_code(* mode_tree_key_cb)(void *, void *, u_int)
Definition: tmux.h:2887
u_int(* mode_tree_height_cb)(void *, u_int)
Definition: tmux.h:2886
unsigned long long key_code
Definition: tmux.h:177
#define KEYC_NONE
Definition: tmux.h:113
cmd_parse_status
Definition: tmux.h:1483
@ CMD_PARSE_ERROR
Definition: tmux.h:1485
void(* mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *, u_int, u_int)
Definition: tmux.h:2882
#define PROMPT_NOFORMAT
Definition: tmux.h:1737
void(* mode_tree_each_cb)(void *, void *, struct client *, key_code)
Definition: tmux.h:2888
#define screen_size_y(s)
Definition: tmux.h:882
#define GRID_ATTR_BRIGHT
Definition: tmux.h:650
#define PANE_REDRAW
Definition: tmux.h:978
#define screen_size_x(s)
Definition: tmux.h:881
u_int utf8_cstrwidth(const char *)
Definition: utf8.c:517
void(* mode_tree_menu_cb)(void *, struct client *, key_code)
Definition: tmux.h:2885
@ KEYC_DOUBLECLICK1_PANE
Definition: tmux.h:214
@ KEYC_HOME
Definition: tmux.h:239
@ KEYC_UP
Definition: tmux.h:246
@ KEYC_MOUSEDOWN3_PANE
Definition: tmux.h:199
@ KEYC_END
Definition: tmux.h:240
@ KEYC_LEFT
Definition: tmux.h:248
@ KEYC_PPAGE
Definition: tmux.h:242
@ KEYC_DOWN
Definition: tmux.h:247
@ KEYC_WHEELDOWN_PANE
Definition: tmux.h:210
@ KEYC_MOUSEDOWN1_PANE
Definition: tmux.h:197
@ KEYC_WHEELUP_PANE
Definition: tmux.h:209
@ KEYC_NPAGE
Definition: tmux.h:241
@ KEYC_RIGHT
Definition: tmux.h:249
void(* mode_tree_build_cb)(void *, struct mode_tree_sort_criteria *, uint64_t *, const char *)
Definition: tmux.h:2880
int window_zoom(struct window_pane *)
Definition: window.c:583
int(* mode_tree_search_cb)(void *, void *, const char *)
Definition: tmux.h:2884
const char * command
Definition: window-copy.c:2244
void * xmalloc(size_t size)
Definition: xmalloc.c:27
void * xreallocarray(void *ptr, size_t nmemb, size_t size)
Definition: xmalloc.c:61
int xasprintf(char **ret, const char *fmt,...)
Definition: xmalloc.c:109
void * xcalloc(size_t nmemb, size_t size)
Definition: xmalloc.c:41
char * xstrdup(const char *str)
Definition: xmalloc.c:89