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