Annotation of src/usr.bin/tmux/window-choose.c, Revision 1.86
1.86 ! nicm 1: /* $OpenBSD: window-choose.c,v 1.85 2017/01/24 21:50:23 nicm Exp $ */
1.1 nicm 2:
3: /*
1.74 nicm 4: * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 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:
1.19 nicm 21: #include <ctype.h>
1.21 nicm 22: #include <stdlib.h>
1.1 nicm 23: #include <string.h>
24:
25: #include "tmux.h"
26:
1.79 nicm 27: static struct screen *window_choose_init(struct window_pane *);
28: static void window_choose_free(struct window_pane *);
29: static void window_choose_resize(struct window_pane *, u_int, u_int);
30: static void window_choose_key(struct window_pane *, struct client *,
31: struct session *, key_code, struct mouse_event *);
32:
33: static void window_choose_default_callback(struct window_choose_data *);
34: static struct window_choose_mode_item *window_choose_get_item(
35: struct window_pane *, key_code, struct mouse_event *);
36:
37: static void window_choose_fire_callback(struct window_pane *,
38: struct window_choose_data *);
39: static void window_choose_redraw_screen(struct window_pane *);
40: static void window_choose_write_line(struct window_pane *,
41: struct screen_write_ctx *, u_int);
42:
43: static void window_choose_scroll_up(struct window_pane *);
44: static void window_choose_scroll_down(struct window_pane *);
45:
46: static void window_choose_collapse(struct window_pane *, struct session *,
47: u_int);
48: static void window_choose_expand(struct window_pane *, struct session *,
49: u_int);
50: static void window_choose_collapse_all(struct window_pane *);
1.1 nicm 51:
1.79 nicm 52: static void window_choose_data_free(struct window_choose_data *);
1.27 nicm 53:
1.22 nicm 54: enum window_choose_input_type {
55: WINDOW_CHOOSE_NORMAL = -1,
56: WINDOW_CHOOSE_GOTO_ITEM,
57: };
58:
1.1 nicm 59: const struct window_mode window_choose_mode = {
1.78 nicm 60: .init = window_choose_init,
61: .free = window_choose_free,
62: .resize = window_choose_resize,
63: .key = window_choose_key,
1.64 nicm 64: };
65:
66: struct window_choose_mode_item {
67: struct window_choose_data *wcd;
68: char *name;
69: int pos;
70: int state;
71: #define TREE_EXPANDED 0x1
1.1 nicm 72: };
73:
74: struct window_choose_mode_data {
75: struct screen screen;
76:
1.75 nicm 77: struct window_choose_mode_item *list;
78: u_int list_size;
79: struct window_choose_mode_item *old_list;
80: u_int old_list_size;
81:
1.22 nicm 82: int width;
1.1 nicm 83: u_int top;
84: u_int selected;
1.22 nicm 85: enum window_choose_input_type input_type;
86: const char *input_prompt;
87: char *input_str;
1.1 nicm 88:
1.19 nicm 89: void (*callbackfn)(struct window_choose_data *);
1.1 nicm 90: };
91:
1.85 nicm 92: static const char window_choose_keys_emacs[] = "0123456789"
93: "abcdefghijklmnoprstuvwxyz"
94: "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
95: static const char window_choose_keys_vi[] = "0123456789"
96: "abcdefhilmnoprstuvwxyz"
97: "ABCDEFIJKMNOPQRSTUVWXYZ";
98:
1.79 nicm 99: static void window_choose_free1(struct window_choose_mode_data *);
1.85 nicm 100: static int window_choose_key_index(struct window_pane *, u_int);
101: static int window_choose_index_key(struct window_pane *, key_code);
1.79 nicm 102: static void window_choose_prompt_input(enum window_choose_input_type,
103: const char *, struct window_pane *, key_code);
104: static void window_choose_reset_top(struct window_pane *, u_int);
1.11 nicm 105:
1.1 nicm 106: void
1.19 nicm 107: window_choose_add(struct window_pane *wp, struct window_choose_data *wcd)
1.1 nicm 108: {
109: struct window_choose_mode_data *data = wp->modedata;
110: struct window_choose_mode_item *item;
1.75 nicm 111: char tmp[11];
1.1 nicm 112:
1.75 nicm 113: data->list = xreallocarray(data->list, data->list_size + 1,
114: sizeof *data->list);
115: item = &data->list[data->list_size++];
1.1 nicm 116:
1.19 nicm 117: item->name = format_expand(wcd->ft, wcd->ft_template);
118: item->wcd = wcd;
1.75 nicm 119: item->pos = data->list_size - 1;
1.27 nicm 120: item->state = 0;
1.22 nicm 121:
1.75 nicm 122: data->width = xsnprintf(tmp, sizeof tmp, "%d", item->pos);
1.1 nicm 123: }
124:
125: void
1.47 nicm 126: window_choose_set_current(struct window_pane *wp, u_int cur)
1.1 nicm 127: {
128: struct window_choose_mode_data *data = wp->modedata;
129: struct screen *s = &data->screen;
130:
131: data->selected = cur;
1.48 nicm 132: window_choose_reset_top(wp, screen_size_y(s));
133: }
134:
1.79 nicm 135: static void
1.48 nicm 136: window_choose_reset_top(struct window_pane *wp, u_int sy)
137: {
138: struct window_choose_mode_data *data = wp->modedata;
139:
140: data->top = 0;
141: if (data->selected > sy - 1)
142: data->top = data->selected - (sy - 1);
1.1 nicm 143:
1.47 nicm 144: window_choose_redraw_screen(wp);
145: }
146:
147: void
148: window_choose_ready(struct window_pane *wp, u_int cur,
149: void (*callbackfn)(struct window_choose_data *))
150: {
151: struct window_choose_mode_data *data = wp->modedata;
1.75 nicm 152: u_int size;
1.47 nicm 153:
1.3 nicm 154: data->callbackfn = callbackfn;
1.33 nicm 155: if (data->callbackfn == NULL)
156: data->callbackfn = window_choose_default_callback;
1.1 nicm 157:
1.75 nicm 158: size = data->old_list_size;
159: data->old_list_size += data->list_size;
160: data->old_list = xreallocarray(data->old_list, data->old_list_size,
161: sizeof *data->old_list);
162: memcpy(data->old_list + size, data->list, data->list_size *
163: sizeof *data->list);
1.27 nicm 164:
1.47 nicm 165: window_choose_set_current(wp, cur);
1.27 nicm 166: window_choose_collapse_all(wp);
1.1 nicm 167: }
168:
1.79 nicm 169: static struct screen *
1.1 nicm 170: window_choose_init(struct window_pane *wp)
171: {
172: struct window_choose_mode_data *data;
173: struct screen *s;
174:
1.75 nicm 175: wp->modedata = data = xcalloc(1, sizeof *data);
1.3 nicm 176:
177: data->callbackfn = NULL;
1.22 nicm 178: data->input_type = WINDOW_CHOOSE_NORMAL;
179: data->input_str = xstrdup("");
180: data->input_prompt = NULL;
1.3 nicm 181:
1.75 nicm 182: data->list = NULL;
183: data->list_size = 0;
184:
185: data->old_list = NULL;
186: data->old_list_size = 0;
187:
1.1 nicm 188: data->top = 0;
189:
190: s = &data->screen;
191: screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
192: s->mode &= ~MODE_CURSOR;
193:
194: return (s);
195: }
196:
1.19 nicm 197: struct window_choose_data *
1.33 nicm 198: window_choose_data_create(int type, struct client *c, struct session *s)
1.19 nicm 199: {
200: struct window_choose_data *wcd;
201:
202: wcd = xmalloc(sizeof *wcd);
1.33 nicm 203: wcd->type = type;
204:
1.86 ! nicm 205: wcd->ft = format_create(NULL, FORMAT_NONE, 0);
1.19 nicm 206: wcd->ft_template = NULL;
1.33 nicm 207:
1.20 nicm 208: wcd->command = NULL;
1.33 nicm 209:
1.24 nicm 210: wcd->wl = NULL;
1.33 nicm 211: wcd->pane_id = -1;
212: wcd->idx = -1;
213:
1.27 nicm 214: wcd->tree_session = NULL;
1.33 nicm 215:
216: wcd->start_client = c;
217: wcd->start_client->references++;
218: wcd->start_session = s;
219: wcd->start_session->references++;
1.19 nicm 220:
221: return (wcd);
222: }
223:
1.79 nicm 224: static void
1.33 nicm 225: window_choose_data_free(struct window_choose_data *wcd)
226: {
1.65 nicm 227: server_client_unref(wcd->start_client);
228: session_unref(wcd->start_session);
1.33 nicm 229:
230: if (wcd->tree_session != NULL)
1.65 nicm 231: session_unref(wcd->tree_session);
1.33 nicm 232:
233: free(wcd->ft_template);
234: format_free(wcd->ft);
235:
236: free(wcd->command);
237: free(wcd);
238: }
239:
240: void
241: window_choose_data_run(struct window_choose_data *cdata)
242: {
1.82 nicm 243: struct cmd_list *cmdlist;
244: char *cause;
245: struct cmdq_item *item;
1.33 nicm 246:
247: /*
248: * The command template will have already been replaced. But if it's
249: * NULL, bail here.
250: */
251: if (cdata->command == NULL)
252: return;
253:
1.84 nicm 254: cmdlist = cmd_string_parse(cdata->command, NULL, 0, &cause);
255: if (cmdlist == NULL) {
1.33 nicm 256: if (cause != NULL) {
257: *cause = toupper((u_char) *cause);
258: status_message_set(cdata->start_client, "%s", cause);
259: free(cause);
260: }
261: return;
262: }
263:
1.82 nicm 264: item = cmdq_get_command(cmdlist, NULL, NULL, 0);
265: cmdq_append(cdata->start_client, item);
1.33 nicm 266: cmd_list_free(cmdlist);
267: }
268:
1.79 nicm 269: static void
1.33 nicm 270: window_choose_default_callback(struct window_choose_data *wcd)
271: {
272: if (wcd == NULL)
273: return;
274: if (wcd->start_client->flags & CLIENT_DEAD)
275: return;
276:
277: window_choose_data_run(wcd);
278: }
279:
1.79 nicm 280: static void
1.1 nicm 281: window_choose_free(struct window_pane *wp)
282: {
1.38 nicm 283: if (wp->modedata != NULL)
284: window_choose_free1(wp->modedata);
285: }
286:
1.79 nicm 287: static void
1.38 nicm 288: window_choose_free1(struct window_choose_mode_data *data)
289: {
1.19 nicm 290: struct window_choose_mode_item *item;
1.1 nicm 291: u_int i;
292:
1.38 nicm 293: if (data == NULL)
294: return;
295:
1.75 nicm 296: for (i = 0; i < data->old_list_size; i++) {
297: item = &data->old_list[i];
1.33 nicm 298: window_choose_data_free(item->wcd);
1.21 nicm 299: free(item->name);
1.19 nicm 300: }
1.75 nicm 301: free(data->list);
302: free(data->old_list);
303:
1.22 nicm 304: free(data->input_str);
1.1 nicm 305:
306: screen_free(&data->screen);
1.21 nicm 307: free(data);
1.1 nicm 308: }
309:
1.79 nicm 310: static void
1.1 nicm 311: window_choose_resize(struct window_pane *wp, u_int sx, u_int sy)
312: {
313: struct window_choose_mode_data *data = wp->modedata;
314: struct screen *s = &data->screen;
315:
1.48 nicm 316: window_choose_reset_top(wp, sy);
1.32 nicm 317: screen_resize(s, sx, sy, 0);
1.1 nicm 318: window_choose_redraw_screen(wp);
319: }
320:
1.79 nicm 321: static void
1.73 nicm 322: window_choose_fire_callback(struct window_pane *wp,
323: struct window_choose_data *wcd)
1.18 nicm 324: {
325: struct window_choose_mode_data *data = wp->modedata;
326:
1.38 nicm 327: wp->modedata = NULL;
328: window_pane_reset_mode(wp);
1.18 nicm 329:
1.19 nicm 330: data->callbackfn(wcd);
1.18 nicm 331:
1.38 nicm 332: window_choose_free1(data);
1.18 nicm 333: }
334:
1.79 nicm 335: static void
1.22 nicm 336: window_choose_prompt_input(enum window_choose_input_type input_type,
1.68 nicm 337: const char *prompt, struct window_pane *wp, key_code key)
1.22 nicm 338: {
339: struct window_choose_mode_data *data = wp->modedata;
340: size_t input_len;
341:
342: data->input_type = input_type;
343: data->input_prompt = prompt;
344: input_len = strlen(data->input_str) + 2;
345:
1.55 nicm 346: data->input_str = xrealloc(data->input_str, input_len);
1.22 nicm 347: data->input_str[input_len - 2] = key;
348: data->input_str[input_len - 1] = '\0';
349:
350: window_choose_redraw_screen(wp);
351: }
352:
1.79 nicm 353: static void
1.61 nicm 354: window_choose_collapse(struct window_pane *wp, struct session *s, u_int pos)
1.27 nicm 355: {
356: struct window_choose_mode_data *data = wp->modedata;
1.75 nicm 357: struct window_choose_mode_item *item, *chosen, *copy = NULL;
1.27 nicm 358: struct window_choose_data *wcd;
1.75 nicm 359: u_int i, copy_size = 0;
1.27 nicm 360:
1.75 nicm 361: chosen = &data->list[pos];
1.27 nicm 362: chosen->state &= ~TREE_EXPANDED;
363:
364: /*
365: * Trying to mangle the &data->list in-place has lots of problems, so
366: * assign the actual result we want to render and copy the new one over
367: * the top of it.
368: */
1.75 nicm 369: for (i = 0; i < data->list_size; i++) {
370: item = &data->list[i];
1.27 nicm 371: wcd = item->wcd;
372:
373: if (s == wcd->tree_session) {
374: /* We only show the session when collapsed. */
375: if (wcd->type & TREE_SESSION) {
376: item->state &= ~TREE_EXPANDED;
1.75 nicm 377:
378: copy = xreallocarray(copy, copy_size + 1,
379: sizeof *copy);
380: memcpy(©[copy_size], item, sizeof *copy);
381: copy_size++;
1.27 nicm 382:
383: /*
384: * Update the selection to this session item so
385: * we don't end up highlighting a non-existent
386: * item.
387: */
388: data->selected = i;
389: }
1.75 nicm 390: } else {
391: copy = xreallocarray(copy, copy_size + 1, sizeof *copy);
392: memcpy(©[copy_size], item, sizeof *copy);
393: copy_size++;
394: }
1.27 nicm 395: }
396:
1.75 nicm 397: if (copy_size != 0) {
398: free(data->list);
399: data->list = copy;
400: data->list_size = copy_size;
1.27 nicm 401: }
402: }
403:
1.79 nicm 404: static void
1.27 nicm 405: window_choose_collapse_all(struct window_pane *wp)
406: {
407: struct window_choose_mode_data *data = wp->modedata;
1.28 nicm 408: struct window_choose_mode_item *item;
1.48 nicm 409: struct screen *scr = &data->screen;
1.28 nicm 410: struct session *s, *chosen;
1.27 nicm 411: u_int i;
412:
1.75 nicm 413: chosen = data->list[data->selected].wcd->start_session;
1.27 nicm 414:
415: RB_FOREACH(s, sessions, &sessions)
1.61 nicm 416: window_choose_collapse(wp, s, data->selected);
1.27 nicm 417:
418: /* Reset the selection back to the starting session. */
1.75 nicm 419: for (i = 0; i < data->list_size; i++) {
420: item = &data->list[i];
1.27 nicm 421:
1.28 nicm 422: if (chosen != item->wcd->tree_session)
1.27 nicm 423: continue;
424:
425: if (item->wcd->type & TREE_SESSION)
426: data->selected = i;
427: }
1.48 nicm 428: window_choose_reset_top(wp, screen_size_y(scr));
1.27 nicm 429: }
430:
431: void
432: window_choose_expand_all(struct window_pane *wp)
433: {
434: struct window_choose_mode_data *data = wp->modedata;
435: struct window_choose_mode_item *item;
1.48 nicm 436: struct screen *scr = &data->screen;
1.27 nicm 437: struct session *s;
438: u_int i;
439:
440: RB_FOREACH(s, sessions, &sessions) {
1.75 nicm 441: for (i = 0; i < data->list_size; i++) {
442: item = &data->list[i];
1.27 nicm 443:
444: if (s != item->wcd->tree_session)
445: continue;
446:
447: if (item->wcd->type & TREE_SESSION)
448: window_choose_expand(wp, s, i);
449: }
450: }
451:
1.48 nicm 452: window_choose_reset_top(wp, screen_size_y(scr));
1.27 nicm 453: }
454:
1.79 nicm 455: static void
1.27 nicm 456: window_choose_expand(struct window_pane *wp, struct session *s, u_int pos)
457: {
458: struct window_choose_mode_data *data = wp->modedata;
459: struct window_choose_mode_item *item, *chosen;
460: struct window_choose_data *wcd;
461: u_int i, items;
462:
1.75 nicm 463: chosen = &data->list[pos];
464: items = data->old_list_size - 1;
1.27 nicm 465:
466: /* It's not possible to expand anything other than sessions. */
467: if (!(chosen->wcd->type & TREE_SESSION))
468: return;
469:
470: /* Don't re-expand a session which is already expanded. */
471: if (chosen->state & TREE_EXPANDED)
472: return;
473:
474: /* Mark the session entry as expanded. */
475: chosen->state |= TREE_EXPANDED;
476:
477: /*
478: * Go back through the original list of all sessions and windows, and
479: * pull out the windows where the session matches the selection chosen
480: * to expand.
481: */
482: for (i = items; i > 0; i--) {
1.75 nicm 483: item = &data->old_list[i];
1.27 nicm 484: item->state |= TREE_EXPANDED;
485: wcd = item->wcd;
486:
487: if (s == wcd->tree_session) {
488: /*
489: * Since the session is already displayed, we only care
490: * to add back in window for it.
491: */
492: if (wcd->type & TREE_WINDOW) {
493: /*
494: * If the insertion point for adding the
495: * windows to the session falls inside the
496: * range of the list, then we insert these
497: * entries in order *AFTER* the selected
498: * session.
499: */
1.75 nicm 500: if (pos < i) {
501: data->list = xreallocarray(data->list,
502: data->list_size + 1,
503: sizeof *data->list);
504: memmove(&data->list[pos + 2],
505: &data->list[pos + 1],
1.76 nicm 506: (data->list_size - (pos + 1)) *
1.75 nicm 507: sizeof *data->list);
508: memcpy(&data->list[pos + 1],
509: &data->old_list[i],
510: sizeof *data->list);
511: data->list_size++;
1.27 nicm 512: } else {
513: /* Ran out of room, add to the end. */
1.75 nicm 514: data->list = xreallocarray(data->list,
515: data->list_size + 1,
516: sizeof *data->list);
517: memcpy(&data->list[data->list_size],
518: &data->old_list[i],
519: sizeof *data->list);
520: data->list_size++;
1.27 nicm 521: }
522: }
523: }
524: }
525: }
526:
1.79 nicm 527: static struct window_choose_mode_item *
1.68 nicm 528: window_choose_get_item(struct window_pane *wp, key_code key,
529: struct mouse_event *m)
1.61 nicm 530: {
531: struct window_choose_mode_data *data = wp->modedata;
532: u_int x, y, idx;
533:
534: if (!KEYC_IS_MOUSE(key))
1.75 nicm 535: return (&data->list[data->selected]);
1.61 nicm 536:
537: if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
538: return (NULL);
539:
540: idx = data->top + y;
1.75 nicm 541: if (idx >= data->list_size)
1.61 nicm 542: return (NULL);
1.75 nicm 543: return (&data->list[idx]);
1.61 nicm 544: }
545:
1.85 nicm 546: static key_code
547: window_choose_translate_key(key_code key)
548: {
549: switch (key) {
550: case '0'|KEYC_ESCAPE:
551: case '1'|KEYC_ESCAPE:
552: case '2'|KEYC_ESCAPE:
553: case '3'|KEYC_ESCAPE:
554: case '4'|KEYC_ESCAPE:
555: case '5'|KEYC_ESCAPE:
556: case '6'|KEYC_ESCAPE:
557: case '7'|KEYC_ESCAPE:
558: case '8'|KEYC_ESCAPE:
559: case '9'|KEYC_ESCAPE:
560: case '\003': /* C-c */
561: case 'q':
562: case '\n':
563: case '\r':
564: case KEYC_BSPACE:
565: case ' ':
566: case KEYC_LEFT:
567: case KEYC_RIGHT:
568: case KEYC_LEFT|KEYC_CTRL:
569: case KEYC_RIGHT|KEYC_CTRL:
570: case KEYC_MOUSEDOWN1_PANE:
571: case KEYC_MOUSEDOWN3_PANE:
572: case KEYC_WHEELUP_PANE:
573: case KEYC_WHEELDOWN_PANE:
574: return (key);
575: case '\031': /* C-y */
576: case KEYC_UP|KEYC_CTRL:
577: return (KEYC_UP|KEYC_CTRL);
578: case '\002': /* C-b */
579: case KEYC_PPAGE:
580: return (KEYC_PPAGE);
581: case '\005': /* C-e */
582: case KEYC_DOWN|KEYC_CTRL:
583: return (KEYC_DOWN|KEYC_CTRL);
584: case '\006': /* C-f */
585: case KEYC_NPAGE:
586: return (KEYC_NPAGE);
587: case 'j':
588: case KEYC_DOWN:
589: return (KEYC_DOWN);
590: case 'k':
591: case KEYC_UP:
592: return (KEYC_UP);
593: case 'g':
594: case KEYC_HOME:
595: return (KEYC_HOME);
596: case 'G':
597: case KEYC_END:
598: return (KEYC_END);
599: case 'H':
600: return ('R'|KEYC_ESCAPE);
601: case 'L':
602: return ('r'|KEYC_ESCAPE);
603: }
604: if ((key >= '0' && key <= '9') ||
605: (key >= 'a' && key <= 'z') ||
606: (key >= 'A' && key <= 'Z'))
607: return (key);
608: return (KEYC_NONE);
609: }
610:
1.79 nicm 611: static void
1.70 nicm 612: window_choose_key(struct window_pane *wp, __unused struct client *c,
1.85 nicm 613: __unused struct session *sp, key_code key, struct mouse_event *m)
1.1 nicm 614: {
615: struct window_choose_mode_data *data = wp->modedata;
616: struct screen *s = &data->screen;
617: struct screen_write_ctx ctx;
618: struct window_choose_mode_item *item;
1.22 nicm 619: size_t input_len;
1.27 nicm 620: u_int items, n;
1.85 nicm 621: int idx, keys;
1.1 nicm 622:
1.85 nicm 623: keys = options_get_number(wp->window->options, "mode-keys");
624: if (keys == MODEKEY_VI) {
625: key = window_choose_translate_key(key);
626: if (key == KEYC_NONE)
627: return;
628: }
1.75 nicm 629: items = data->list_size;
1.1 nicm 630:
1.25 nicm 631: if (data->input_type == WINDOW_CHOOSE_GOTO_ITEM) {
1.85 nicm 632: switch (key) {
633: case '\003': /* C-c */
634: case '\033': /* Escape */
635: case 'q':
1.25 nicm 636: data->input_type = WINDOW_CHOOSE_NORMAL;
637: window_choose_redraw_screen(wp);
1.22 nicm 638: break;
1.85 nicm 639: case '\n':
640: case '\r':
1.22 nicm 641: n = strtonum(data->input_str, 0, INT_MAX, NULL);
1.25 nicm 642: if (n > items - 1) {
643: data->input_type = WINDOW_CHOOSE_NORMAL;
644: window_choose_redraw_screen(wp);
1.22 nicm 645: break;
1.25 nicm 646: }
1.75 nicm 647: window_choose_fire_callback(wp, data->list[n].wcd);
1.22 nicm 648: break;
1.85 nicm 649: case KEYC_BSPACE:
1.25 nicm 650: input_len = strlen(data->input_str);
651: if (input_len > 0)
652: data->input_str[input_len - 1] = '\0';
653: window_choose_redraw_screen(wp);
654: break;
655: default:
656: if (key < '0' || key > '9')
657: break;
658: window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
659: "Goto Item", wp, key);
660: break;
1.22 nicm 661: }
1.25 nicm 662: return;
663: }
664:
1.85 nicm 665: switch (key) {
666: case '\003': /* C-c */
667: case '\033': /* Escape */
668: case 'q':
1.25 nicm 669: window_choose_fire_callback(wp, NULL);
670: break;
1.85 nicm 671: case '\n':
672: case '\r':
673: case KEYC_MOUSEDOWN1_PANE:
1.61 nicm 674: if ((item = window_choose_get_item(wp, key, m)) == NULL)
675: break;
1.25 nicm 676: window_choose_fire_callback(wp, item->wcd);
1.1 nicm 677: break;
1.85 nicm 678: case ' ':
679: case KEYC_MOUSEDOWN3_PANE:
1.61 nicm 680: if ((item = window_choose_get_item(wp, key, m)) == NULL)
681: break;
682: if (item->state & TREE_EXPANDED) {
683: window_choose_collapse(wp, item->wcd->tree_session,
1.62 nicm 684: data->selected);
1.61 nicm 685: } else {
1.27 nicm 686: window_choose_expand(wp, item->wcd->tree_session,
1.62 nicm 687: data->selected);
1.27 nicm 688: }
689: window_choose_redraw_screen(wp);
690: break;
1.85 nicm 691: case KEYC_LEFT:
1.61 nicm 692: if ((item = window_choose_get_item(wp, key, m)) == NULL)
693: break;
1.27 nicm 694: if (item->state & TREE_EXPANDED) {
1.61 nicm 695: window_choose_collapse(wp, item->wcd->tree_session,
696: data->selected);
1.27 nicm 697: window_choose_redraw_screen(wp);
698: }
699: break;
1.85 nicm 700: case KEYC_LEFT|KEYC_CTRL:
1.27 nicm 701: window_choose_collapse_all(wp);
702: break;
1.85 nicm 703: case KEYC_RIGHT:
1.61 nicm 704: if ((item = window_choose_get_item(wp, key, m)) == NULL)
705: break;
1.27 nicm 706: if (!(item->state & TREE_EXPANDED)) {
707: window_choose_expand(wp, item->wcd->tree_session,
708: data->selected);
709: window_choose_redraw_screen(wp);
710: }
711: break;
1.85 nicm 712: case KEYC_RIGHT|KEYC_CTRL:
1.27 nicm 713: window_choose_expand_all(wp);
714: break;
1.85 nicm 715: case '\020': /* C-p */
716: case KEYC_UP:
717: case KEYC_WHEELUP_PANE:
1.1 nicm 718: if (items == 0)
719: break;
720: if (data->selected == 0) {
721: data->selected = items - 1;
722: if (data->selected > screen_size_y(s) - 1)
723: data->top = items - screen_size_y(s);
724: window_choose_redraw_screen(wp);
725: break;
726: }
727: data->selected--;
728: if (data->selected < data->top)
729: window_choose_scroll_up(wp);
730: else {
731: screen_write_start(&ctx, wp, NULL);
1.73 nicm 732: window_choose_write_line(wp, &ctx,
733: data->selected - data->top);
734: window_choose_write_line(wp, &ctx,
735: data->selected + 1 - data->top);
1.1 nicm 736: screen_write_stop(&ctx);
737: }
738: break;
1.85 nicm 739: case '\016': /* C-n */
740: case KEYC_DOWN:
741: case KEYC_WHEELDOWN_PANE:
1.1 nicm 742: if (items == 0)
743: break;
744: if (data->selected == items - 1) {
745: data->selected = 0;
746: data->top = 0;
747: window_choose_redraw_screen(wp);
748: break;
749: }
750: data->selected++;
1.5 nicm 751:
1.15 nicm 752: if (data->selected < data->top + screen_size_y(s)) {
1.1 nicm 753: screen_write_start(&ctx, wp, NULL);
1.73 nicm 754: window_choose_write_line(wp, &ctx,
755: data->selected - data->top);
756: window_choose_write_line(wp, &ctx,
757: data->selected - 1 - data->top);
1.1 nicm 758: screen_write_stop(&ctx);
1.15 nicm 759: } else
760: window_choose_scroll_down(wp);
761: break;
1.85 nicm 762: case KEYC_UP|KEYC_CTRL:
1.15 nicm 763: if (items == 0 || data->top == 0)
764: break;
765: if (data->selected == data->top + screen_size_y(s) - 1) {
766: data->selected--;
767: window_choose_scroll_up(wp);
768: screen_write_start(&ctx, wp, NULL);
1.73 nicm 769: window_choose_write_line(wp, &ctx,
770: screen_size_y(s) - 1);
1.15 nicm 771: screen_write_stop(&ctx);
772: } else
773: window_choose_scroll_up(wp);
774: break;
1.85 nicm 775: case KEYC_DOWN|KEYC_CTRL:
1.15 nicm 776: if (items == 0 ||
777: data->top + screen_size_y(&data->screen) >= items)
778: break;
779: if (data->selected == data->top) {
780: data->selected++;
781: window_choose_scroll_down(wp);
782: screen_write_start(&ctx, wp, NULL);
783: window_choose_write_line(wp, &ctx, 0);
784: screen_write_stop(&ctx);
785: } else
786: window_choose_scroll_down(wp);
1.1 nicm 787: break;
1.85 nicm 788: case KEYC_PPAGE:
1.1 nicm 789: if (data->selected < screen_size_y(s)) {
790: data->selected = 0;
791: data->top = 0;
792: } else {
793: data->selected -= screen_size_y(s);
794: if (data->top < screen_size_y(s))
795: data->top = 0;
796: else
797: data->top -= screen_size_y(s);
798: }
1.13 nicm 799: window_choose_redraw_screen(wp);
1.1 nicm 800: break;
1.85 nicm 801: case KEYC_NPAGE:
1.1 nicm 802: data->selected += screen_size_y(s);
803: if (data->selected > items - 1)
804: data->selected = items - 1;
805: data->top += screen_size_y(s);
806: if (screen_size_y(s) < items) {
807: if (data->top + screen_size_y(s) > items)
808: data->top = items - screen_size_y(s);
809: } else
810: data->top = 0;
811: if (data->selected < data->top)
812: data->top = data->selected;
813: window_choose_redraw_screen(wp);
814: break;
1.85 nicm 815: case KEYC_BSPACE:
1.22 nicm 816: input_len = strlen(data->input_str);
817: if (input_len > 0)
818: data->input_str[input_len - 1] = '\0';
819: window_choose_redraw_screen(wp);
820: break;
1.85 nicm 821: case '0'|KEYC_ESCAPE:
822: case '1'|KEYC_ESCAPE:
823: case '2'|KEYC_ESCAPE:
824: case '3'|KEYC_ESCAPE:
825: case '4'|KEYC_ESCAPE:
826: case '5'|KEYC_ESCAPE:
827: case '6'|KEYC_ESCAPE:
828: case '7'|KEYC_ESCAPE:
829: case '8'|KEYC_ESCAPE:
830: case '9'|KEYC_ESCAPE:
1.25 nicm 831: key &= KEYC_MASK_KEY;
832: if (key < '0' || key > '9')
1.11 nicm 833: break;
1.25 nicm 834: window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
835: "Goto Item", wp, key);
1.51 nicm 836: break;
1.85 nicm 837: case KEYC_HOME:
838: case '<'|KEYC_ESCAPE:
1.51 nicm 839: data->selected = 0;
840: data->top = 0;
841: window_choose_redraw_screen(wp);
842: break;
1.85 nicm 843: case 'R'|KEYC_ESCAPE:
1.51 nicm 844: data->selected = data->top;
845: window_choose_redraw_screen(wp);
846: break;
1.85 nicm 847: case 'r'|KEYC_ESCAPE:
1.51 nicm 848: data->selected = data->top + screen_size_y(s) - 1;
849: if (data->selected > items - 1)
850: data->selected = items - 1;
851: window_choose_redraw_screen(wp);
852: break;
1.85 nicm 853: case KEYC_END:
854: case '>'|KEYC_ESCAPE:
1.51 nicm 855: data->selected = items - 1;
856: if (screen_size_y(s) < items)
857: data->top = items - screen_size_y(s);
858: else
859: data->top = 0;
860: window_choose_redraw_screen(wp);
1.22 nicm 861: break;
862: default:
1.85 nicm 863: idx = window_choose_index_key(wp, key);
1.75 nicm 864: if (idx < 0 || (u_int) idx >= data->list_size)
1.25 nicm 865: break;
866: data->selected = idx;
1.75 nicm 867: window_choose_fire_callback(wp, data->list[idx].wcd);
1.1 nicm 868: break;
869: }
870: }
871:
1.79 nicm 872: static void
1.68 nicm 873: window_choose_write_line(struct window_pane *wp, struct screen_write_ctx *ctx,
874: u_int py)
1.1 nicm 875: {
876: struct window_choose_mode_data *data = wp->modedata;
877: struct window_choose_mode_item *item;
1.67 nicm 878: struct options *oo = wp->window->options;
1.1 nicm 879: struct screen *s = &data->screen;
880: struct grid_cell gc;
1.22 nicm 881: size_t last, xoff = 0;
1.25 nicm 882: char hdr[32], label[32];
1.69 nicm 883: int key;
1.1 nicm 884:
1.3 nicm 885: if (data->callbackfn == NULL)
1.1 nicm 886: fatalx("called before callback assigned");
887:
1.22 nicm 888: last = screen_size_y(s) - 1;
1.1 nicm 889: memcpy(&gc, &grid_default_cell, sizeof gc);
1.83 nicm 890: gc.flags |= GRID_FLAG_NOPALETTE;
1.22 nicm 891: if (data->selected == data->top + py)
1.50 nicm 892: style_apply(&gc, oo, "mode-style");
1.1 nicm 893:
894: screen_write_cursormove(ctx, 0, py);
1.75 nicm 895: if (data->top + py < data->list_size) {
896: item = &data->list[data->top + py];
1.24 nicm 897: if (item->wcd->wl != NULL &&
898: item->wcd->wl->flags & WINLINK_ALERTFLAGS)
899: gc.attr |= GRID_ATTR_BRIGHT;
1.11 nicm 900:
1.85 nicm 901: key = window_choose_key_index(wp, data->top + py);
1.25 nicm 902: if (key != -1)
1.56 nicm 903: xsnprintf(label, sizeof label, "(%c)", key);
1.25 nicm 904: else
1.56 nicm 905: xsnprintf(label, sizeof label, "(%d)", item->pos);
1.69 nicm 906: screen_write_nputs(ctx, screen_size_x(s) - 1, &gc,
1.27 nicm 907: "%*s %s %s", data->width + 2, label,
908: /*
909: * Add indication to tree if necessary about whether it's
910: * expanded or not.
911: */
912: (item->wcd->type & TREE_SESSION) ?
913: (item->state & TREE_EXPANDED ? "-" : "+") : "", item->name);
1.1 nicm 914: }
1.40 nicm 915: while (s->cx < screen_size_x(s) - 1)
1.1 nicm 916: screen_write_putc(ctx, &gc, ' ');
1.11 nicm 917:
1.22 nicm 918: if (data->input_type != WINDOW_CHOOSE_NORMAL) {
1.50 nicm 919: style_apply(&gc, oo, "mode-style");
1.11 nicm 920:
1.22 nicm 921: xoff = xsnprintf(hdr, sizeof hdr,
922: "%s: %s", data->input_prompt, data->input_str);
923: screen_write_cursormove(ctx, 0, last);
924: screen_write_puts(ctx, &gc, "%s", hdr);
925: screen_write_cursormove(ctx, xoff, py);
926: memcpy(&gc, &grid_default_cell, sizeof gc);
1.11 nicm 927: }
1.22 nicm 928:
1.11 nicm 929: }
930:
1.79 nicm 931: static int
1.85 nicm 932: window_choose_key_index(struct window_pane *wp, u_int idx)
1.25 nicm 933: {
1.85 nicm 934: const char *ptr;
935: int keys;
936:
937: keys = options_get_number(wp->window->options, "mode-keys");
938: if (keys == MODEKEY_VI)
939: ptr = window_choose_keys_vi;
940: else
941: ptr = window_choose_keys_emacs;
942: for (; *ptr != '\0'; ptr++) {
1.25 nicm 943: if (idx-- == 0)
944: return (*ptr);
945: }
946: return (-1);
947: }
948:
1.79 nicm 949: static int
1.85 nicm 950: window_choose_index_key(struct window_pane *wp, key_code key)
1.11 nicm 951: {
1.85 nicm 952: const char *ptr;
953: int keys;
954: u_int idx = 0;
955:
956: keys = options_get_number(wp->window->options, "mode-keys");
957: if (keys == MODEKEY_VI)
958: ptr = window_choose_keys_vi;
959: else
960: ptr = window_choose_keys_emacs;
961: for (; *ptr != '\0'; ptr++) {
1.68 nicm 962: if (key == (key_code)*ptr)
1.11 nicm 963: return (idx);
964: idx++;
965: }
966: return (-1);
1.1 nicm 967: }
968:
1.79 nicm 969: static void
1.1 nicm 970: window_choose_redraw_screen(struct window_pane *wp)
971: {
972: struct window_choose_mode_data *data = wp->modedata;
973: struct screen *s = &data->screen;
974: struct screen_write_ctx ctx;
975: u_int i;
976:
977: screen_write_start(&ctx, wp, NULL);
978: for (i = 0; i < screen_size_y(s); i++)
979: window_choose_write_line(wp, &ctx, i);
980: screen_write_stop(&ctx);
981: }
982:
1.79 nicm 983: static void
1.1 nicm 984: window_choose_scroll_up(struct window_pane *wp)
985: {
986: struct window_choose_mode_data *data = wp->modedata;
987: struct screen_write_ctx ctx;
988:
989: if (data->top == 0)
990: return;
991: data->top--;
992:
993: screen_write_start(&ctx, wp, NULL);
994: screen_write_cursormove(&ctx, 0, 0);
1.80 nicm 995: screen_write_insertline(&ctx, 1, 8);
1.1 nicm 996: window_choose_write_line(wp, &ctx, 0);
997: if (screen_size_y(&data->screen) > 1)
998: window_choose_write_line(wp, &ctx, 1);
999: screen_write_stop(&ctx);
1000: }
1001:
1.79 nicm 1002: static void
1.1 nicm 1003: window_choose_scroll_down(struct window_pane *wp)
1004: {
1005: struct window_choose_mode_data *data = wp->modedata;
1006: struct screen *s = &data->screen;
1007: struct screen_write_ctx ctx;
1008:
1.75 nicm 1009: if (data->top >= data->list_size)
1.1 nicm 1010: return;
1011: data->top++;
1012:
1013: screen_write_start(&ctx, wp, NULL);
1014: screen_write_cursormove(&ctx, 0, 0);
1.80 nicm 1015: screen_write_deleteline(&ctx, 1, 8);
1.1 nicm 1016: window_choose_write_line(wp, &ctx, screen_size_y(s) - 1);
1017: if (screen_size_y(&data->screen) > 1)
1018: window_choose_write_line(wp, &ctx, screen_size_y(s) - 2);
1019: screen_write_stop(&ctx);
1.19 nicm 1020: }
1021:
1.20 nicm 1022: struct window_choose_data *
1.35 nicm 1023: window_choose_add_session(struct window_pane *wp, struct client *c,
1.36 nicm 1024: struct session *s, const char *template, const char *action, u_int idx)
1.20 nicm 1025: {
1026: struct window_choose_data *wcd;
1027:
1.33 nicm 1028: wcd = window_choose_data_create(TREE_SESSION, c, c->session);
1.46 nicm 1029: wcd->idx = s->id;
1.33 nicm 1030:
1.27 nicm 1031: wcd->tree_session = s;
1.33 nicm 1032: wcd->tree_session->references++;
1033:
1.20 nicm 1034: wcd->ft_template = xstrdup(template);
1035: format_add(wcd->ft, "line", "%u", idx);
1.59 nicm 1036: format_defaults(wcd->ft, NULL, s, NULL, NULL);
1.20 nicm 1037:
1.33 nicm 1038: wcd->command = cmd_template_replace(action, s->name, 1);
1.20 nicm 1039:
1040: window_choose_add(wp, wcd);
1041:
1042: return (wcd);
1043: }
1044:
1045: struct window_choose_data *
1.35 nicm 1046: window_choose_add_window(struct window_pane *wp, struct client *c,
1.22 nicm 1047: struct session *s, struct winlink *wl, const char *template,
1.36 nicm 1048: const char *action, u_int idx)
1.20 nicm 1049: {
1050: struct window_choose_data *wcd;
1.33 nicm 1051: char *expanded;
1.20 nicm 1052:
1.33 nicm 1053: wcd = window_choose_data_create(TREE_WINDOW, c, c->session);
1054: wcd->idx = wl->idx;
1.20 nicm 1055:
1.33 nicm 1056: wcd->wl = wl;
1.20 nicm 1057:
1.27 nicm 1058: wcd->tree_session = s;
1.33 nicm 1059: wcd->tree_session->references++;
1060:
1.20 nicm 1061: wcd->ft_template = xstrdup(template);
1062: format_add(wcd->ft, "line", "%u", idx);
1.59 nicm 1063: format_defaults(wcd->ft, NULL, s, wl, NULL);
1.20 nicm 1064:
1.33 nicm 1065: xasprintf(&expanded, "%s:%d", s->name, wl->idx);
1066: wcd->command = cmd_template_replace(action, expanded, 1);
1067: free(expanded);
1.20 nicm 1068:
1069: window_choose_add(wp, wcd);
1070:
1071: return (wcd);
1.1 nicm 1072: }