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