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