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