Annotation of src/usr.bin/tmux/window-choose.c, Revision 1.23
1.23 ! nicm 1: /* $OpenBSD: window-choose.c,v 1.22 2012/08/11 06:45:33 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
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:
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);
1.16 nicm 30: void window_choose_key(struct window_pane *, struct session *, int);
1.1 nicm 31: void window_choose_mouse(
1.16 nicm 32: struct window_pane *, struct session *, struct mouse_event *);
1.1 nicm 33:
1.19 nicm 34: void window_choose_fire_callback(
35: struct window_pane *, struct window_choose_data *);
1.1 nicm 36: void window_choose_redraw_screen(struct window_pane *);
37: void window_choose_write_line(
1.13 nicm 38: struct window_pane *, struct screen_write_ctx *, u_int);
1.1 nicm 39:
40: void window_choose_scroll_up(struct window_pane *);
41: void window_choose_scroll_down(struct window_pane *);
42:
1.22 nicm 43: enum window_choose_input_type {
44: WINDOW_CHOOSE_NORMAL = -1,
45: WINDOW_CHOOSE_GOTO_ITEM,
46: };
47:
1.1 nicm 48: const struct window_mode window_choose_mode = {
49: window_choose_init,
50: window_choose_free,
51: window_choose_resize,
52: window_choose_key,
53: window_choose_mouse,
54: NULL,
55: };
56:
57: struct window_choose_mode_data {
58: struct screen screen;
59:
60: struct mode_key_data mdata;
61:
62: ARRAY_DECL(, struct window_choose_mode_item) list;
1.22 nicm 63: int width;
1.1 nicm 64: u_int top;
65: u_int selected;
1.22 nicm 66: enum window_choose_input_type input_type;
67: const char *input_prompt;
68: char *input_str;
1.1 nicm 69:
1.19 nicm 70: void (*callbackfn)(struct window_choose_data *);
71: void (*freefn)(struct window_choose_data *);
1.1 nicm 72: };
73:
1.22 nicm 74: int window_choose_index_key(int);
75: void window_choose_prompt_input(enum window_choose_input_type,
76: const char *, struct window_pane *, int);
1.11 nicm 77:
1.1 nicm 78: void
1.19 nicm 79: window_choose_add(struct window_pane *wp, struct window_choose_data *wcd)
1.1 nicm 80: {
81: struct window_choose_mode_data *data = wp->modedata;
82: struct window_choose_mode_item *item;
1.22 nicm 83: char tmp[10];
1.1 nicm 84:
85: ARRAY_EXPAND(&data->list, 1);
86: item = &ARRAY_LAST(&data->list);
87:
1.19 nicm 88: item->name = format_expand(wcd->ft, wcd->ft_template);
89: item->wcd = wcd;
1.22 nicm 90: item->pos = ARRAY_LENGTH(&data->list) - 1;
91:
1.23 ! nicm 92: data->width = snprintf (tmp, sizeof tmp , "%u", item->pos);
1.1 nicm 93: }
94:
95: void
1.3 nicm 96: window_choose_ready(struct window_pane *wp, u_int cur,
1.19 nicm 97: void (*callbackfn)(struct window_choose_data *),
98: void (*freefn)(struct window_choose_data *))
1.1 nicm 99: {
100: struct window_choose_mode_data *data = wp->modedata;
101: struct screen *s = &data->screen;
102:
103: data->selected = cur;
104: if (data->selected > screen_size_y(s) - 1)
105: data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s);
106:
1.3 nicm 107: data->callbackfn = callbackfn;
108: data->freefn = freefn;
1.1 nicm 109:
110: window_choose_redraw_screen(wp);
111: }
112:
113: struct screen *
114: window_choose_init(struct window_pane *wp)
115: {
116: struct window_choose_mode_data *data;
117: struct screen *s;
1.5 nicm 118: int keys;
1.1 nicm 119:
120: wp->modedata = data = xmalloc(sizeof *data);
1.3 nicm 121:
122: data->callbackfn = NULL;
123: data->freefn = NULL;
1.22 nicm 124: data->input_type = WINDOW_CHOOSE_NORMAL;
125: data->input_str = xstrdup("");
126: data->input_prompt = NULL;
1.3 nicm 127:
1.1 nicm 128: ARRAY_INIT(&data->list);
129: data->top = 0;
130:
131: s = &data->screen;
132: screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
133: s->mode &= ~MODE_CURSOR;
1.7 nicm 134: if (options_get_number(&wp->window->options, "mode-mouse"))
1.17 nicm 135: s->mode |= MODE_MOUSE_STANDARD;
1.1 nicm 136:
1.5 nicm 137: keys = options_get_number(&wp->window->options, "mode-keys");
138: if (keys == MODEKEY_EMACS)
1.6 nicm 139: mode_key_init(&data->mdata, &mode_key_tree_emacs_choice);
1.5 nicm 140: else
1.6 nicm 141: mode_key_init(&data->mdata, &mode_key_tree_vi_choice);
1.1 nicm 142:
143: return (s);
144: }
145:
1.19 nicm 146: struct window_choose_data *
147: window_choose_data_create(struct cmd_ctx *ctx)
148: {
149: struct window_choose_data *wcd;
150:
151: wcd = xmalloc(sizeof *wcd);
152: wcd->ft = format_create();
153: wcd->ft_template = NULL;
1.20 nicm 154: wcd->command = NULL;
1.19 nicm 155: wcd->client = ctx->curclient;
156: wcd->session = ctx->curclient->session;
157: wcd->idx = -1;
158:
159: return (wcd);
160: }
161:
1.1 nicm 162: void
163: window_choose_free(struct window_pane *wp)
164: {
165: struct window_choose_mode_data *data = wp->modedata;
1.19 nicm 166: struct window_choose_mode_item *item;
1.1 nicm 167: u_int i;
168:
1.19 nicm 169: for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
170: item = &ARRAY_ITEM(&data->list, i);
171: if (data->freefn != NULL && item->wcd != NULL)
172: data->freefn(item->wcd);
1.21 nicm 173: free(item->name);
1.19 nicm 174: }
1.1 nicm 175: ARRAY_FREE(&data->list);
1.22 nicm 176: free(data->input_str);
1.1 nicm 177:
178: screen_free(&data->screen);
1.21 nicm 179: free(data);
1.1 nicm 180: }
181:
182: void
183: window_choose_resize(struct window_pane *wp, u_int sx, u_int sy)
184: {
185: struct window_choose_mode_data *data = wp->modedata;
186: struct screen *s = &data->screen;
187:
188: data->top = 0;
189: if (data->selected > sy - 1)
190: data->top = data->selected - (sy - 1);
191:
192: screen_resize(s, sx, sy);
193: window_choose_redraw_screen(wp);
194: }
195:
1.18 nicm 196: void
1.19 nicm 197: window_choose_fire_callback(
198: struct window_pane *wp, struct window_choose_data *wcd)
1.18 nicm 199: {
200: struct window_choose_mode_data *data = wp->modedata;
201: const struct window_mode *oldmode;
202:
203: oldmode = wp->mode;
204: wp->mode = NULL;
205:
1.19 nicm 206: data->callbackfn(wcd);
1.18 nicm 207:
208: wp->mode = oldmode;
209: }
210:
1.22 nicm 211: void
212: window_choose_prompt_input(enum window_choose_input_type input_type,
213: const char *prompt, struct window_pane *wp, int key)
214: {
215: struct window_choose_mode_data *data = wp->modedata;
216: size_t input_len;
217:
218: data->input_type = input_type;
219: data->input_prompt = prompt;
220: input_len = strlen(data->input_str) + 2;
221:
222: data->input_str = xrealloc(data->input_str, 1, input_len);
223: data->input_str[input_len - 2] = key;
224: data->input_str[input_len - 1] = '\0';
225:
226: window_choose_redraw_screen(wp);
227: }
228:
1.12 nicm 229: /* ARGSUSED */
1.1 nicm 230: void
1.16 nicm 231: window_choose_key(struct window_pane *wp, unused struct session *sess, int key)
1.1 nicm 232: {
233: struct window_choose_mode_data *data = wp->modedata;
234: struct screen *s = &data->screen;
235: struct screen_write_ctx ctx;
236: struct window_choose_mode_item *item;
1.22 nicm 237: size_t input_len;
238: u_int items, n;
1.11 nicm 239: int idx;
1.1 nicm 240:
241: items = ARRAY_LENGTH(&data->list);
242:
243: switch (mode_key_lookup(&data->mdata, key)) {
1.5 nicm 244: case MODEKEYCHOICE_CANCEL:
1.19 nicm 245: window_choose_fire_callback(wp, NULL);
1.1 nicm 246: window_pane_reset_mode(wp);
247: break;
1.5 nicm 248: case MODEKEYCHOICE_CHOOSE:
1.22 nicm 249: switch (data->input_type) {
250: case WINDOW_CHOOSE_NORMAL:
251: item = &ARRAY_ITEM(&data->list, data->selected);
252: window_choose_fire_callback(wp, item->wcd);
253: window_pane_reset_mode(wp);
254: break;
255: case WINDOW_CHOOSE_GOTO_ITEM:
256: n = strtonum(data->input_str, 0, INT_MAX, NULL);
257: if (n > items - 1)
258: break;
259: item = &ARRAY_ITEM(&data->list, n);
260: window_choose_fire_callback(wp, item->wcd);
261: window_pane_reset_mode(wp);
262: break;
263: }
1.1 nicm 264: break;
1.5 nicm 265: case MODEKEYCHOICE_UP:
1.1 nicm 266: if (items == 0)
267: break;
268: if (data->selected == 0) {
269: data->selected = items - 1;
270: if (data->selected > screen_size_y(s) - 1)
271: data->top = items - screen_size_y(s);
272: window_choose_redraw_screen(wp);
273: break;
274: }
275: data->selected--;
276: if (data->selected < data->top)
277: window_choose_scroll_up(wp);
278: else {
279: screen_write_start(&ctx, wp, NULL);
280: window_choose_write_line(
281: wp, &ctx, data->selected - data->top);
282: window_choose_write_line(
283: wp, &ctx, data->selected + 1 - data->top);
284: screen_write_stop(&ctx);
285: }
286: break;
1.5 nicm 287: case MODEKEYCHOICE_DOWN:
1.1 nicm 288: if (items == 0)
289: break;
290: if (data->selected == items - 1) {
291: data->selected = 0;
292: data->top = 0;
293: window_choose_redraw_screen(wp);
294: break;
295: }
296: data->selected++;
1.5 nicm 297:
1.15 nicm 298: if (data->selected < data->top + screen_size_y(s)) {
1.1 nicm 299: screen_write_start(&ctx, wp, NULL);
300: window_choose_write_line(
301: wp, &ctx, data->selected - data->top);
302: window_choose_write_line(
303: wp, &ctx, data->selected - 1 - data->top);
304: screen_write_stop(&ctx);
1.15 nicm 305: } else
306: window_choose_scroll_down(wp);
307: break;
308: case MODEKEYCHOICE_SCROLLUP:
309: if (items == 0 || data->top == 0)
310: break;
311: if (data->selected == data->top + screen_size_y(s) - 1) {
312: data->selected--;
313: window_choose_scroll_up(wp);
314: screen_write_start(&ctx, wp, NULL);
315: window_choose_write_line(
316: wp, &ctx, screen_size_y(s) - 1);
317: screen_write_stop(&ctx);
318: } else
319: window_choose_scroll_up(wp);
320: break;
321: case MODEKEYCHOICE_SCROLLDOWN:
322: if (items == 0 ||
323: data->top + screen_size_y(&data->screen) >= items)
324: break;
325: if (data->selected == data->top) {
326: data->selected++;
327: window_choose_scroll_down(wp);
328: screen_write_start(&ctx, wp, NULL);
329: window_choose_write_line(wp, &ctx, 0);
330: screen_write_stop(&ctx);
331: } else
332: window_choose_scroll_down(wp);
1.1 nicm 333: break;
1.5 nicm 334: case MODEKEYCHOICE_PAGEUP:
1.1 nicm 335: if (data->selected < screen_size_y(s)) {
336: data->selected = 0;
337: data->top = 0;
338: } else {
339: data->selected -= screen_size_y(s);
340: if (data->top < screen_size_y(s))
341: data->top = 0;
342: else
343: data->top -= screen_size_y(s);
344: }
1.13 nicm 345: window_choose_redraw_screen(wp);
1.1 nicm 346: break;
1.5 nicm 347: case MODEKEYCHOICE_PAGEDOWN:
1.1 nicm 348: data->selected += screen_size_y(s);
349: if (data->selected > items - 1)
350: data->selected = items - 1;
351: data->top += screen_size_y(s);
352: if (screen_size_y(s) < items) {
353: if (data->top + screen_size_y(s) > items)
354: data->top = items - screen_size_y(s);
355: } else
356: data->top = 0;
357: if (data->selected < data->top)
358: data->top = data->selected;
359: window_choose_redraw_screen(wp);
360: break;
1.22 nicm 361: case MODEKEYCHOICE_BACKSPACE:
362: input_len = strlen(data->input_str);
363: if (input_len > 0)
364: data->input_str[input_len - 1] = '\0';
365: window_choose_redraw_screen(wp);
366: break;
367: case MODEKEYCHOICE_STARTNUMBERPREFIX:
368: if (key < '0' && key > '9')
1.11 nicm 369: break;
1.13 nicm 370:
1.22 nicm 371: /*
372: * If there's less than ten items (0-9) then pressing a number
373: * will automatically select that item; otherwise, prompt for
374: * the item to go to.
375: */
376: if (ARRAY_LENGTH(&data->list) - 1 <= 9) {
377: idx = window_choose_index_key(key);
378: if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list))
379: break;
380: data->selected = idx;
381:
382: item = &ARRAY_ITEM(&data->list, data->selected);
383: window_choose_fire_callback(wp, item->wcd);
384: window_pane_reset_mode(wp);
385: } else {
386: window_choose_prompt_input(
387: WINDOW_CHOOSE_GOTO_ITEM, "Goto item", wp, key);
388: }
389: break;
390: default:
1.1 nicm 391: break;
392: }
393: }
394:
1.12 nicm 395: /* ARGSUSED */
1.1 nicm 396: void
1.10 nicm 397: window_choose_mouse(
1.16 nicm 398: struct window_pane *wp, unused struct session *sess, struct mouse_event *m)
1.1 nicm 399: {
400: struct window_choose_mode_data *data = wp->modedata;
401: struct screen *s = &data->screen;
402: struct window_choose_mode_item *item;
403: u_int idx;
404:
1.10 nicm 405: if ((m->b & 3) == 3)
1.1 nicm 406: return;
1.10 nicm 407: if (m->x >= screen_size_x(s))
1.1 nicm 408: return;
1.10 nicm 409: if (m->y >= screen_size_y(s))
1.1 nicm 410: return;
411:
1.10 nicm 412: idx = data->top + m->y;
1.1 nicm 413: if (idx >= ARRAY_LENGTH(&data->list))
414: return;
415: data->selected = idx;
416:
417: item = &ARRAY_ITEM(&data->list, data->selected);
1.19 nicm 418: window_choose_fire_callback(wp, item->wcd);
1.1 nicm 419: window_pane_reset_mode(wp);
420: }
421:
422: void
423: window_choose_write_line(
424: struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
425: {
426: struct window_choose_mode_data *data = wp->modedata;
427: struct window_choose_mode_item *item;
1.9 nicm 428: struct options *oo = &wp->window->options;
1.1 nicm 429: struct screen *s = &data->screen;
430: struct grid_cell gc;
1.22 nicm 431: size_t last, xoff = 0;
432: char hdr[32];
433: int utf8flag;
1.1 nicm 434:
1.3 nicm 435: if (data->callbackfn == NULL)
1.1 nicm 436: fatalx("called before callback assigned");
437:
1.22 nicm 438: last = screen_size_y(s) - 1;
1.2 nicm 439: utf8flag = options_get_number(&wp->window->options, "utf8");
1.1 nicm 440: memcpy(&gc, &grid_default_cell, sizeof gc);
1.22 nicm 441: if (data->selected == data->top + py)
442: window_mode_attrs(&gc, oo);
1.1 nicm 443:
444: screen_write_cursormove(ctx, 0, py);
445: if (data->top + py < ARRAY_LENGTH(&data->list)) {
446: item = &ARRAY_ITEM(&data->list, data->top + py);
1.22 nicm 447: screen_write_nputs(ctx, screen_size_x(s) - 1,
448: &gc, utf8flag, "(%*d) %s", data->width,
449: item->pos, item->name);
1.11 nicm 450:
1.1 nicm 451: }
452: while (s->cx < screen_size_x(s))
453: screen_write_putc(ctx, &gc, ' ');
1.11 nicm 454:
1.22 nicm 455: if (data->input_type != WINDOW_CHOOSE_NORMAL) {
456: window_mode_attrs(&gc, oo);
1.11 nicm 457:
1.22 nicm 458: xoff = xsnprintf(hdr, sizeof hdr,
459: "%s: %s", data->input_prompt, data->input_str);
460: screen_write_cursormove(ctx, 0, last);
461: screen_write_puts(ctx, &gc, "%s", hdr);
462: screen_write_cursormove(ctx, xoff, py);
463: memcpy(&gc, &grid_default_cell, sizeof gc);
1.11 nicm 464: }
1.22 nicm 465:
1.11 nicm 466: }
467:
468: int
1.22 nicm 469: window_choose_index_key(int key)
1.11 nicm 470: {
1.22 nicm 471: static const char keys[] = "0123456789";
1.11 nicm 472: const char *ptr;
473: u_int idx = 0;
474:
475: for (ptr = keys; *ptr != '\0'; ptr++) {
476: if (key == *ptr)
477: return (idx);
478: idx++;
479: }
480: return (-1);
1.1 nicm 481: }
482:
483: void
484: window_choose_redraw_screen(struct window_pane *wp)
485: {
486: struct window_choose_mode_data *data = wp->modedata;
487: struct screen *s = &data->screen;
488: struct screen_write_ctx ctx;
489: u_int i;
490:
491: screen_write_start(&ctx, wp, NULL);
492: for (i = 0; i < screen_size_y(s); i++)
493: window_choose_write_line(wp, &ctx, i);
494: screen_write_stop(&ctx);
495: }
496:
497: void
498: window_choose_scroll_up(struct window_pane *wp)
499: {
500: struct window_choose_mode_data *data = wp->modedata;
501: struct screen_write_ctx ctx;
502:
503: if (data->top == 0)
504: return;
505: data->top--;
506:
507: screen_write_start(&ctx, wp, NULL);
508: screen_write_cursormove(&ctx, 0, 0);
509: screen_write_insertline(&ctx, 1);
510: window_choose_write_line(wp, &ctx, 0);
511: if (screen_size_y(&data->screen) > 1)
512: window_choose_write_line(wp, &ctx, 1);
513: screen_write_stop(&ctx);
514: }
515:
516: void
517: window_choose_scroll_down(struct window_pane *wp)
518: {
519: struct window_choose_mode_data *data = wp->modedata;
520: struct screen *s = &data->screen;
521: struct screen_write_ctx ctx;
522:
523: if (data->top >= ARRAY_LENGTH(&data->list))
524: return;
525: data->top++;
526:
527: screen_write_start(&ctx, wp, NULL);
528: screen_write_cursormove(&ctx, 0, 0);
529: screen_write_deleteline(&ctx, 1);
530: window_choose_write_line(wp, &ctx, screen_size_y(s) - 1);
531: if (screen_size_y(&data->screen) > 1)
532: window_choose_write_line(wp, &ctx, screen_size_y(s) - 2);
533: screen_write_stop(&ctx);
1.19 nicm 534: }
535:
536: void
537: window_choose_ctx(struct window_choose_data *cdata)
538: {
539: struct cmd_ctx ctx;
540: struct cmd_list *cmdlist;
1.20 nicm 541: char *cause;
1.19 nicm 542:
1.20 nicm 543: /* The command template will have already been replaced. But if it's
544: * NULL, bail here.
545: */
546: if (cdata->command == NULL)
547: return;
1.19 nicm 548:
1.20 nicm 549: if (cmd_string_parse(cdata->command, &cmdlist, &cause) != 0) {
1.19 nicm 550: if (cause != NULL) {
551: *cause = toupper((u_char) *cause);
552: status_message_set(cdata->client, "%s", cause);
1.21 nicm 553: free(cause);
1.19 nicm 554: }
555: return;
556: }
557:
558: ctx.msgdata = NULL;
559: ctx.curclient = cdata->client;
560:
561: ctx.error = key_bindings_error;
562: ctx.print = key_bindings_print;
563: ctx.info = key_bindings_info;
564:
565: ctx.cmdclient = NULL;
566:
567: cmd_list_exec(cmdlist, &ctx);
568: cmd_list_free(cmdlist);
1.20 nicm 569: }
570:
571: struct window_choose_data *
572: window_choose_add_session(struct window_pane *wp, struct cmd_ctx *ctx,
1.22 nicm 573: struct session *s, const char *template, char *action, u_int idx)
1.20 nicm 574: {
575: struct window_choose_data *wcd;
576:
577: wcd = window_choose_data_create(ctx);
578: wcd->idx = s->idx;
579: wcd->command = cmd_template_replace(action, s->name, 1);
580: wcd->ft_template = xstrdup(template);
581: format_add(wcd->ft, "line", "%u", idx);
582: format_session(wcd->ft, s);
583:
584: wcd->client->references++;
585: wcd->session->references++;
586:
587: window_choose_add(wp, wcd);
588:
589: return (wcd);
590: }
591:
592: struct window_choose_data *
593: window_choose_add_window(struct window_pane *wp, struct cmd_ctx *ctx,
1.22 nicm 594: struct session *s, struct winlink *wl, const char *template,
595: char *action, u_int idx)
1.20 nicm 596: {
597: struct window_choose_data *wcd;
598: char *action_data;
599:
600: wcd = window_choose_data_create(ctx);
601:
602: xasprintf(&action_data, "%s:%d", s->name, wl->idx);
603: wcd->command = cmd_template_replace(action, action_data, 1);
1.21 nicm 604: free(action_data);
1.20 nicm 605:
606: wcd->idx = wl->idx;
607: wcd->ft_template = xstrdup(template);
608: format_add(wcd->ft, "line", "%u", idx);
609: format_session(wcd->ft, s);
610: format_winlink(wcd->ft, s, wl);
611: format_window_pane(wcd->ft, wl->window->active);
612:
613: wcd->client->references++;
614: wcd->session->references++;
615:
616: window_choose_add(wp, wcd);
617:
618: return (wcd);
1.1 nicm 619: }