Fix detach -a by skipping clients where the session is NULL.
[tmux-openbsd.git] / window-choose.c
blob70c20085a4b6a4266096f964f42eb4f56f98744d
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
25 #include "tmux.h"
27 struct screen *window_choose_init(struct window_pane *);
28 void window_choose_free(struct window_pane *);
29 void window_choose_resize(struct window_pane *, u_int, u_int);
30 void window_choose_key(struct window_pane *, struct session *, int);
31 void window_choose_mouse(
32 struct window_pane *, struct session *, struct mouse_event *);
34 void window_choose_default_callback(struct window_choose_data *);
36 void window_choose_fire_callback(
37 struct window_pane *, struct window_choose_data *);
38 void window_choose_redraw_screen(struct window_pane *);
39 void window_choose_write_line(
40 struct window_pane *, struct screen_write_ctx *, u_int);
42 void window_choose_scroll_up(struct window_pane *);
43 void window_choose_scroll_down(struct window_pane *);
45 void window_choose_collapse(struct window_pane *, struct session *);
46 void window_choose_expand(struct window_pane *, struct session *, u_int);
48 enum window_choose_input_type {
49 WINDOW_CHOOSE_NORMAL = -1,
50 WINDOW_CHOOSE_GOTO_ITEM,
53 const struct window_mode window_choose_mode = {
54 window_choose_init,
55 window_choose_free,
56 window_choose_resize,
57 window_choose_key,
58 window_choose_mouse,
59 NULL,
62 struct window_choose_mode_data {
63 struct screen screen;
65 struct mode_key_data mdata;
67 ARRAY_DECL(, struct window_choose_mode_item) list;
68 ARRAY_DECL(, struct window_choose_mode_item) old_list;
69 int width;
70 u_int top;
71 u_int selected;
72 enum window_choose_input_type input_type;
73 const char *input_prompt;
74 char *input_str;
76 void (*callbackfn)(struct window_choose_data *);
79 void window_choose_free1(struct window_choose_mode_data *);
80 int window_choose_key_index(struct window_choose_mode_data *, u_int);
81 int window_choose_index_key(struct window_choose_mode_data *, int);
82 void window_choose_prompt_input(enum window_choose_input_type,
83 const char *, struct window_pane *, int);
84 void window_choose_reset_top(struct window_pane *, u_int);
86 void
87 window_choose_add(struct window_pane *wp, struct window_choose_data *wcd)
89 struct window_choose_mode_data *data = wp->modedata;
90 struct window_choose_mode_item *item;
91 char tmp[10];
93 ARRAY_EXPAND(&data->list, 1);
94 item = &ARRAY_LAST(&data->list);
96 item->name = format_expand(wcd->ft, wcd->ft_template);
97 item->wcd = wcd;
98 item->pos = ARRAY_LENGTH(&data->list) - 1;
99 item->state = 0;
101 data->width = xsnprintf (tmp, sizeof tmp , "%u", item->pos);
104 void
105 window_choose_set_current(struct window_pane *wp, u_int cur)
107 struct window_choose_mode_data *data = wp->modedata;
108 struct screen *s = &data->screen;
110 data->selected = cur;
111 window_choose_reset_top(wp, screen_size_y(s));
114 void
115 window_choose_reset_top(struct window_pane *wp, u_int sy)
117 struct window_choose_mode_data *data = wp->modedata;
119 data->top = 0;
120 if (data->selected > sy - 1)
121 data->top = data->selected - (sy - 1);
123 window_choose_redraw_screen(wp);
126 void
127 window_choose_ready(struct window_pane *wp, u_int cur,
128 void (*callbackfn)(struct window_choose_data *))
130 struct window_choose_mode_data *data = wp->modedata;
132 data->callbackfn = callbackfn;
133 if (data->callbackfn == NULL)
134 data->callbackfn = window_choose_default_callback;
136 ARRAY_CONCAT(&data->old_list, &data->list);
138 window_choose_set_current(wp, cur);
139 window_choose_collapse_all(wp);
142 struct screen *
143 window_choose_init(struct window_pane *wp)
145 struct window_choose_mode_data *data;
146 struct screen *s;
147 int keys;
149 wp->modedata = data = xmalloc(sizeof *data);
151 data->callbackfn = NULL;
152 data->input_type = WINDOW_CHOOSE_NORMAL;
153 data->input_str = xstrdup("");
154 data->input_prompt = NULL;
156 ARRAY_INIT(&data->list);
157 ARRAY_INIT(&data->old_list);
158 data->top = 0;
160 s = &data->screen;
161 screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
162 s->mode &= ~MODE_CURSOR;
163 if (options_get_number(&wp->window->options, "mode-mouse"))
164 s->mode |= MODE_MOUSE_STANDARD;
166 keys = options_get_number(&wp->window->options, "mode-keys");
167 if (keys == MODEKEY_EMACS)
168 mode_key_init(&data->mdata, &mode_key_tree_emacs_choice);
169 else
170 mode_key_init(&data->mdata, &mode_key_tree_vi_choice);
172 return (s);
175 struct window_choose_data *
176 window_choose_data_create(int type, struct client *c, struct session *s)
178 struct window_choose_data *wcd;
180 wcd = xmalloc(sizeof *wcd);
181 wcd->type = type;
183 wcd->ft = format_create();
184 wcd->ft_template = NULL;
186 wcd->command = NULL;
188 wcd->wl = NULL;
189 wcd->pane_id = -1;
190 wcd->idx = -1;
192 wcd->tree_session = NULL;
194 wcd->start_client = c;
195 wcd->start_client->references++;
196 wcd->start_session = s;
197 wcd->start_session->references++;
199 return (wcd);
202 void
203 window_choose_data_free(struct window_choose_data *wcd)
205 wcd->start_client->references--;
206 wcd->start_session->references--;
208 if (wcd->tree_session != NULL)
209 wcd->tree_session->references--;
211 free(wcd->ft_template);
212 format_free(wcd->ft);
214 free(wcd->command);
215 free(wcd);
218 void
219 window_choose_data_run(struct window_choose_data *cdata)
221 struct cmd_list *cmdlist;
222 char *cause;
225 * The command template will have already been replaced. But if it's
226 * NULL, bail here.
228 if (cdata->command == NULL)
229 return;
231 if (cmd_string_parse(cdata->command, &cmdlist, NULL, 0, &cause) != 0) {
232 if (cause != NULL) {
233 *cause = toupper((u_char) *cause);
234 status_message_set(cdata->start_client, "%s", cause);
235 free(cause);
237 return;
240 cmdq_run(cdata->start_client->cmdq, cmdlist);
241 cmd_list_free(cmdlist);
244 void
245 window_choose_default_callback(struct window_choose_data *wcd)
247 if (wcd == NULL)
248 return;
249 if (wcd->start_client->flags & CLIENT_DEAD)
250 return;
252 window_choose_data_run(wcd);
255 void
256 window_choose_free(struct window_pane *wp)
258 if (wp->modedata != NULL)
259 window_choose_free1(wp->modedata);
262 void
263 window_choose_free1(struct window_choose_mode_data *data)
265 struct window_choose_mode_item *item;
266 u_int i;
268 if (data == NULL)
269 return;
271 for (i = 0; i < ARRAY_LENGTH(&data->old_list); i++) {
272 item = &ARRAY_ITEM(&data->old_list, i);
273 window_choose_data_free(item->wcd);
274 free(item->name);
276 ARRAY_FREE(&data->list);
277 ARRAY_FREE(&data->old_list);
278 free(data->input_str);
280 screen_free(&data->screen);
281 free(data);
284 void
285 window_choose_resize(struct window_pane *wp, u_int sx, u_int sy)
287 struct window_choose_mode_data *data = wp->modedata;
288 struct screen *s = &data->screen;
290 window_choose_reset_top(wp, sy);
291 screen_resize(s, sx, sy, 0);
292 window_choose_redraw_screen(wp);
295 void
296 window_choose_fire_callback(
297 struct window_pane *wp, struct window_choose_data *wcd)
299 struct window_choose_mode_data *data = wp->modedata;
301 wp->modedata = NULL;
302 window_pane_reset_mode(wp);
304 data->callbackfn(wcd);
306 window_choose_free1(data);
309 void
310 window_choose_prompt_input(enum window_choose_input_type input_type,
311 const char *prompt, struct window_pane *wp, int key)
313 struct window_choose_mode_data *data = wp->modedata;
314 size_t input_len;
316 data->input_type = input_type;
317 data->input_prompt = prompt;
318 input_len = strlen(data->input_str) + 2;
320 data->input_str = xrealloc(data->input_str, 1, input_len);
321 data->input_str[input_len - 2] = key;
322 data->input_str[input_len - 1] = '\0';
324 window_choose_redraw_screen(wp);
327 void
328 window_choose_collapse(struct window_pane *wp, struct session *s)
330 struct window_choose_mode_data *data = wp->modedata;
331 struct window_choose_mode_item *item, *chosen;
332 struct window_choose_data *wcd;
333 u_int i, pos;
335 ARRAY_DECL(, struct window_choose_mode_item) list_copy;
336 ARRAY_INIT(&list_copy);
338 pos = data->selected;
340 chosen = &ARRAY_ITEM(&data->list, pos);
341 chosen->state &= ~TREE_EXPANDED;
344 * Trying to mangle the &data->list in-place has lots of problems, so
345 * assign the actual result we want to render and copy the new one over
346 * the top of it.
348 for (i = 0; i < ARRAY_LENGTH(&data->list); i++)
350 item = &ARRAY_ITEM(&data->list, i);
351 wcd = item->wcd;
353 if (s == wcd->tree_session) {
354 /* We only show the session when collapsed. */
355 if (wcd->type & TREE_SESSION) {
356 item->state &= ~TREE_EXPANDED;
358 ARRAY_ADD(&list_copy,
359 ARRAY_ITEM(&data->list, i));
361 * Update the selection to this session item so
362 * we don't end up highlighting a non-existent
363 * item.
365 data->selected = i;
367 } else
368 ARRAY_ADD(&list_copy, ARRAY_ITEM(&data->list, i));
371 if (!ARRAY_EMPTY(&list_copy)) {
372 ARRAY_FREE(&data->list);
373 ARRAY_CONCAT(&data->list, &list_copy);
374 ARRAY_FREE(&list_copy);
378 void
379 window_choose_collapse_all(struct window_pane *wp)
381 struct window_choose_mode_data *data = wp->modedata;
382 struct window_choose_mode_item *item;
383 struct screen *scr = &data->screen;
384 struct session *s, *chosen;
385 u_int i;
387 chosen = ARRAY_ITEM(&data->list, data->selected).wcd->start_session;
389 RB_FOREACH(s, sessions, &sessions)
390 window_choose_collapse(wp, s);
392 /* Reset the selection back to the starting session. */
393 for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
394 item = &ARRAY_ITEM(&data->list, i);
396 if (chosen != item->wcd->tree_session)
397 continue;
399 if (item->wcd->type & TREE_SESSION)
400 data->selected = i;
402 window_choose_reset_top(wp, screen_size_y(scr));
405 void
406 window_choose_expand_all(struct window_pane *wp)
408 struct window_choose_mode_data *data = wp->modedata;
409 struct window_choose_mode_item *item;
410 struct screen *scr = &data->screen;
411 struct session *s;
412 u_int i;
414 RB_FOREACH(s, sessions, &sessions) {
415 for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
416 item = &ARRAY_ITEM(&data->list, i);
418 if (s != item->wcd->tree_session)
419 continue;
421 if (item->wcd->type & TREE_SESSION)
422 window_choose_expand(wp, s, i);
426 window_choose_reset_top(wp, screen_size_y(scr));
429 void
430 window_choose_expand(struct window_pane *wp, struct session *s, u_int pos)
432 struct window_choose_mode_data *data = wp->modedata;
433 struct window_choose_mode_item *item, *chosen;
434 struct window_choose_data *wcd;
435 u_int i, items;
437 chosen = &ARRAY_ITEM(&data->list, pos);
438 items = ARRAY_LENGTH(&data->old_list) - 1;
440 /* It's not possible to expand anything other than sessions. */
441 if (!(chosen->wcd->type & TREE_SESSION))
442 return;
444 /* Don't re-expand a session which is already expanded. */
445 if (chosen->state & TREE_EXPANDED)
446 return;
448 /* Mark the session entry as expanded. */
449 chosen->state |= TREE_EXPANDED;
452 * Go back through the original list of all sessions and windows, and
453 * pull out the windows where the session matches the selection chosen
454 * to expand.
456 for (i = items; i > 0; i--) {
457 item = &ARRAY_ITEM(&data->old_list, i);
458 item->state |= TREE_EXPANDED;
459 wcd = item->wcd;
461 if (s == wcd->tree_session) {
463 * Since the session is already displayed, we only care
464 * to add back in window for it.
466 if (wcd->type & TREE_WINDOW) {
468 * If the insertion point for adding the
469 * windows to the session falls inside the
470 * range of the list, then we insert these
471 * entries in order *AFTER* the selected
472 * session.
474 if (pos < i ) {
475 ARRAY_INSERT(&data->list,
476 pos + 1,
477 ARRAY_ITEM(&data->old_list,
478 i));
479 } else {
480 /* Ran out of room, add to the end. */
481 ARRAY_ADD(&data->list,
482 ARRAY_ITEM(&data->old_list,
483 i));
490 void
491 window_choose_key(struct window_pane *wp, unused struct session *sess, int key)
493 struct window_choose_mode_data *data = wp->modedata;
494 struct screen *s = &data->screen;
495 struct screen_write_ctx ctx;
496 struct window_choose_mode_item *item;
497 size_t input_len;
498 u_int items, n;
499 int idx;
501 items = ARRAY_LENGTH(&data->list);
503 if (data->input_type == WINDOW_CHOOSE_GOTO_ITEM) {
504 switch (mode_key_lookup(&data->mdata, key, NULL)) {
505 case MODEKEYCHOICE_CANCEL:
506 data->input_type = WINDOW_CHOOSE_NORMAL;
507 window_choose_redraw_screen(wp);
508 break;
509 case MODEKEYCHOICE_CHOOSE:
510 n = strtonum(data->input_str, 0, INT_MAX, NULL);
511 if (n > items - 1) {
512 data->input_type = WINDOW_CHOOSE_NORMAL;
513 window_choose_redraw_screen(wp);
514 break;
516 item = &ARRAY_ITEM(&data->list, n);
517 window_choose_fire_callback(wp, item->wcd);
518 break;
519 case MODEKEYCHOICE_BACKSPACE:
520 input_len = strlen(data->input_str);
521 if (input_len > 0)
522 data->input_str[input_len - 1] = '\0';
523 window_choose_redraw_screen(wp);
524 break;
525 default:
526 if (key < '0' || key > '9')
527 break;
528 window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
529 "Goto Item", wp, key);
530 break;
532 return;
535 switch (mode_key_lookup(&data->mdata, key, NULL)) {
536 case MODEKEYCHOICE_CANCEL:
537 window_choose_fire_callback(wp, NULL);
538 break;
539 case MODEKEYCHOICE_CHOOSE:
540 item = &ARRAY_ITEM(&data->list, data->selected);
541 window_choose_fire_callback(wp, item->wcd);
542 break;
543 case MODEKEYCHOICE_TREE_TOGGLE:
544 item = &ARRAY_ITEM(&data->list, data->selected);
545 if (item->state & TREE_EXPANDED)
546 window_choose_collapse(wp, item->wcd->tree_session);
547 else {
548 window_choose_expand(wp, item->wcd->tree_session,
549 data->selected);
551 window_choose_redraw_screen(wp);
552 break;
553 case MODEKEYCHOICE_TREE_COLLAPSE:
554 item = &ARRAY_ITEM(&data->list, data->selected);
555 if (item->state & TREE_EXPANDED) {
556 window_choose_collapse(wp, item->wcd->tree_session);
557 window_choose_redraw_screen(wp);
559 break;
560 case MODEKEYCHOICE_TREE_COLLAPSE_ALL:
561 window_choose_collapse_all(wp);
562 break;
563 case MODEKEYCHOICE_TREE_EXPAND:
564 item = &ARRAY_ITEM(&data->list, data->selected);
565 if (!(item->state & TREE_EXPANDED)) {
566 window_choose_expand(wp, item->wcd->tree_session,
567 data->selected);
568 window_choose_redraw_screen(wp);
570 break;
571 case MODEKEYCHOICE_TREE_EXPAND_ALL:
572 window_choose_expand_all(wp);
573 break;
574 case MODEKEYCHOICE_UP:
575 if (items == 0)
576 break;
577 if (data->selected == 0) {
578 data->selected = items - 1;
579 if (data->selected > screen_size_y(s) - 1)
580 data->top = items - screen_size_y(s);
581 window_choose_redraw_screen(wp);
582 break;
584 data->selected--;
585 if (data->selected < data->top)
586 window_choose_scroll_up(wp);
587 else {
588 screen_write_start(&ctx, wp, NULL);
589 window_choose_write_line(
590 wp, &ctx, data->selected - data->top);
591 window_choose_write_line(
592 wp, &ctx, data->selected + 1 - data->top);
593 screen_write_stop(&ctx);
595 break;
596 case MODEKEYCHOICE_DOWN:
597 if (items == 0)
598 break;
599 if (data->selected == items - 1) {
600 data->selected = 0;
601 data->top = 0;
602 window_choose_redraw_screen(wp);
603 break;
605 data->selected++;
607 if (data->selected < data->top + screen_size_y(s)) {
608 screen_write_start(&ctx, wp, NULL);
609 window_choose_write_line(
610 wp, &ctx, data->selected - data->top);
611 window_choose_write_line(
612 wp, &ctx, data->selected - 1 - data->top);
613 screen_write_stop(&ctx);
614 } else
615 window_choose_scroll_down(wp);
616 break;
617 case MODEKEYCHOICE_SCROLLUP:
618 if (items == 0 || data->top == 0)
619 break;
620 if (data->selected == data->top + screen_size_y(s) - 1) {
621 data->selected--;
622 window_choose_scroll_up(wp);
623 screen_write_start(&ctx, wp, NULL);
624 window_choose_write_line(
625 wp, &ctx, screen_size_y(s) - 1);
626 screen_write_stop(&ctx);
627 } else
628 window_choose_scroll_up(wp);
629 break;
630 case MODEKEYCHOICE_SCROLLDOWN:
631 if (items == 0 ||
632 data->top + screen_size_y(&data->screen) >= items)
633 break;
634 if (data->selected == data->top) {
635 data->selected++;
636 window_choose_scroll_down(wp);
637 screen_write_start(&ctx, wp, NULL);
638 window_choose_write_line(wp, &ctx, 0);
639 screen_write_stop(&ctx);
640 } else
641 window_choose_scroll_down(wp);
642 break;
643 case MODEKEYCHOICE_PAGEUP:
644 if (data->selected < screen_size_y(s)) {
645 data->selected = 0;
646 data->top = 0;
647 } else {
648 data->selected -= screen_size_y(s);
649 if (data->top < screen_size_y(s))
650 data->top = 0;
651 else
652 data->top -= screen_size_y(s);
654 window_choose_redraw_screen(wp);
655 break;
656 case MODEKEYCHOICE_PAGEDOWN:
657 data->selected += screen_size_y(s);
658 if (data->selected > items - 1)
659 data->selected = items - 1;
660 data->top += screen_size_y(s);
661 if (screen_size_y(s) < items) {
662 if (data->top + screen_size_y(s) > items)
663 data->top = items - screen_size_y(s);
664 } else
665 data->top = 0;
666 if (data->selected < data->top)
667 data->top = data->selected;
668 window_choose_redraw_screen(wp);
669 break;
670 case MODEKEYCHOICE_BACKSPACE:
671 input_len = strlen(data->input_str);
672 if (input_len > 0)
673 data->input_str[input_len - 1] = '\0';
674 window_choose_redraw_screen(wp);
675 break;
676 case MODEKEYCHOICE_STARTNUMBERPREFIX:
677 key &= KEYC_MASK_KEY;
678 if (key < '0' || key > '9')
679 break;
680 window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
681 "Goto Item", wp, key);
682 break;
683 default:
684 idx = window_choose_index_key(data, key);
685 if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list))
686 break;
687 data->selected = idx;
689 item = &ARRAY_ITEM(&data->list, data->selected);
690 window_choose_fire_callback(wp, item->wcd);
691 break;
695 void
696 window_choose_mouse(
697 struct window_pane *wp, unused struct session *sess, struct mouse_event *m)
699 struct window_choose_mode_data *data = wp->modedata;
700 struct screen *s = &data->screen;
701 struct window_choose_mode_item *item;
702 u_int idx;
704 if (~m->event & MOUSE_EVENT_CLICK)
705 return;
706 if (m->x >= screen_size_x(s))
707 return;
708 if (m->y >= screen_size_y(s))
709 return;
711 idx = data->top + m->y;
712 if (idx >= ARRAY_LENGTH(&data->list))
713 return;
714 data->selected = idx;
716 item = &ARRAY_ITEM(&data->list, data->selected);
717 window_choose_fire_callback(wp, item->wcd);
720 void
721 window_choose_write_line(
722 struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
724 struct window_choose_mode_data *data = wp->modedata;
725 struct window_choose_mode_item *item;
726 struct options *oo = &wp->window->options;
727 struct screen *s = &data->screen;
728 struct grid_cell gc;
729 size_t last, xoff = 0;
730 char hdr[32], label[32];
731 int utf8flag, key;
733 if (data->callbackfn == NULL)
734 fatalx("called before callback assigned");
736 last = screen_size_y(s) - 1;
737 utf8flag = options_get_number(&wp->window->options, "utf8");
738 memcpy(&gc, &grid_default_cell, sizeof gc);
739 if (data->selected == data->top + py)
740 window_mode_attrs(&gc, oo);
742 screen_write_cursormove(ctx, 0, py);
743 if (data->top + py < ARRAY_LENGTH(&data->list)) {
744 item = &ARRAY_ITEM(&data->list, data->top + py);
745 if (item->wcd->wl != NULL &&
746 item->wcd->wl->flags & WINLINK_ALERTFLAGS)
747 gc.attr |= GRID_ATTR_BRIGHT;
749 key = window_choose_key_index(data, data->top + py);
750 if (key != -1)
751 xsnprintf (label, sizeof label, "(%c)", key);
752 else
753 xsnprintf (label, sizeof label, "(%d)", item->pos);
754 screen_write_nputs(ctx, screen_size_x(s) - 1, &gc, utf8flag,
755 "%*s %s %s", data->width + 2, label,
757 * Add indication to tree if necessary about whether it's
758 * expanded or not.
760 (item->wcd->type & TREE_SESSION) ?
761 (item->state & TREE_EXPANDED ? "-" : "+") : "", item->name);
763 while (s->cx < screen_size_x(s) - 1)
764 screen_write_putc(ctx, &gc, ' ');
766 if (data->input_type != WINDOW_CHOOSE_NORMAL) {
767 window_mode_attrs(&gc, oo);
769 xoff = xsnprintf(hdr, sizeof hdr,
770 "%s: %s", data->input_prompt, data->input_str);
771 screen_write_cursormove(ctx, 0, last);
772 screen_write_puts(ctx, &gc, "%s", hdr);
773 screen_write_cursormove(ctx, xoff, py);
774 memcpy(&gc, &grid_default_cell, sizeof gc);
780 window_choose_key_index(struct window_choose_mode_data *data, u_int idx)
782 static const char keys[] = "0123456789"
783 "abcdefghijklmnopqrstuvwxyz"
784 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
785 const char *ptr;
786 int mkey;
788 for (ptr = keys; *ptr != '\0'; ptr++) {
789 mkey = mode_key_lookup(&data->mdata, *ptr, NULL);
790 if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER)
791 continue;
792 if (idx-- == 0)
793 return (*ptr);
795 return (-1);
799 window_choose_index_key(struct window_choose_mode_data *data, int key)
801 static const char keys[] = "0123456789"
802 "abcdefghijklmnopqrstuvwxyz"
803 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
804 const char *ptr;
805 int mkey;
806 u_int idx = 0;
808 for (ptr = keys; *ptr != '\0'; ptr++) {
809 mkey = mode_key_lookup(&data->mdata, *ptr, NULL);
810 if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER)
811 continue;
812 if (key == *ptr)
813 return (idx);
814 idx++;
816 return (-1);
819 void
820 window_choose_redraw_screen(struct window_pane *wp)
822 struct window_choose_mode_data *data = wp->modedata;
823 struct screen *s = &data->screen;
824 struct screen_write_ctx ctx;
825 u_int i;
827 screen_write_start(&ctx, wp, NULL);
828 for (i = 0; i < screen_size_y(s); i++)
829 window_choose_write_line(wp, &ctx, i);
830 screen_write_stop(&ctx);
833 void
834 window_choose_scroll_up(struct window_pane *wp)
836 struct window_choose_mode_data *data = wp->modedata;
837 struct screen_write_ctx ctx;
839 if (data->top == 0)
840 return;
841 data->top--;
843 screen_write_start(&ctx, wp, NULL);
844 screen_write_cursormove(&ctx, 0, 0);
845 screen_write_insertline(&ctx, 1);
846 window_choose_write_line(wp, &ctx, 0);
847 if (screen_size_y(&data->screen) > 1)
848 window_choose_write_line(wp, &ctx, 1);
849 screen_write_stop(&ctx);
852 void
853 window_choose_scroll_down(struct window_pane *wp)
855 struct window_choose_mode_data *data = wp->modedata;
856 struct screen *s = &data->screen;
857 struct screen_write_ctx ctx;
859 if (data->top >= ARRAY_LENGTH(&data->list))
860 return;
861 data->top++;
863 screen_write_start(&ctx, wp, NULL);
864 screen_write_cursormove(&ctx, 0, 0);
865 screen_write_deleteline(&ctx, 1);
866 window_choose_write_line(wp, &ctx, screen_size_y(s) - 1);
867 if (screen_size_y(&data->screen) > 1)
868 window_choose_write_line(wp, &ctx, screen_size_y(s) - 2);
869 screen_write_stop(&ctx);
872 struct window_choose_data *
873 window_choose_add_session(struct window_pane *wp, struct client *c,
874 struct session *s, const char *template, const char *action, u_int idx)
876 struct window_choose_data *wcd;
878 wcd = window_choose_data_create(TREE_SESSION, c, c->session);
879 wcd->idx = s->id;
881 wcd->tree_session = s;
882 wcd->tree_session->references++;
884 wcd->ft_template = xstrdup(template);
885 format_add(wcd->ft, "line", "%u", idx);
886 format_session(wcd->ft, s);
888 wcd->command = cmd_template_replace(action, s->name, 1);
890 window_choose_add(wp, wcd);
892 return (wcd);
895 struct window_choose_data *
896 window_choose_add_item(struct window_pane *wp, struct client *c,
897 struct winlink *wl, const char *template, const char *action, u_int idx)
899 struct window_choose_data *wcd;
900 char *expanded;
902 wcd = window_choose_data_create(TREE_OTHER, c, c->session);
903 wcd->idx = wl->idx;
905 wcd->ft_template = xstrdup(template);
906 format_add(wcd->ft, "line", "%u", idx);
907 format_session(wcd->ft, wcd->start_session);
908 format_winlink(wcd->ft, wcd->start_session, wl);
909 format_window_pane(wcd->ft, wl->window->active);
912 * Interpolate action here, since the data we pass back is the expanded
913 * template itself.
915 xasprintf(&expanded, "%s", format_expand(wcd->ft, wcd->ft_template));
916 wcd->command = cmd_template_replace(action, expanded, 1);
917 free(expanded);
919 window_choose_add(wp, wcd);
921 return (wcd);
925 struct window_choose_data *
926 window_choose_add_window(struct window_pane *wp, struct client *c,
927 struct session *s, struct winlink *wl, const char *template,
928 const char *action, u_int idx)
930 struct window_choose_data *wcd;
931 char *expanded;
933 wcd = window_choose_data_create(TREE_WINDOW, c, c->session);
934 wcd->idx = wl->idx;
936 wcd->wl = wl;
938 wcd->tree_session = s;
939 wcd->tree_session->references++;
941 wcd->ft_template = xstrdup(template);
942 format_add(wcd->ft, "line", "%u", idx);
943 format_session(wcd->ft, s);
944 format_winlink(wcd->ft, s, wl);
945 format_window_pane(wcd->ft, wl->window->active);
947 xasprintf(&expanded, "%s:%d", s->name, wl->idx);
948 wcd->command = cmd_template_replace(action, expanded, 1);
949 free(expanded);
951 window_choose_add(wp, wcd);
953 return (wcd);