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