Annotation of src/usr.bin/tmux/window-choose.c, Revision 1.36
1.36 ! nicm 1: /* $OpenBSD: window-choose.c,v 1.35 2013/03/21 16:09:59 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.33 nicm 34: void window_choose_default_callback(struct window_choose_data *);
35:
1.19 nicm 36: void window_choose_fire_callback(
37: struct window_pane *, struct window_choose_data *);
1.1 nicm 38: void window_choose_redraw_screen(struct window_pane *);
39: void window_choose_write_line(
1.13 nicm 40: struct window_pane *, struct screen_write_ctx *, u_int);
1.1 nicm 41:
42: void window_choose_scroll_up(struct window_pane *);
43: void window_choose_scroll_down(struct window_pane *);
44:
1.27 nicm 45: void window_choose_collapse(struct window_pane *, struct session *);
46: void window_choose_expand(struct window_pane *, struct session *, u_int);
47: void window_choose_collapse_all(struct window_pane *);
48:
1.22 nicm 49: enum window_choose_input_type {
50: WINDOW_CHOOSE_NORMAL = -1,
51: WINDOW_CHOOSE_GOTO_ITEM,
52: };
53:
1.1 nicm 54: const struct window_mode window_choose_mode = {
55: window_choose_init,
56: window_choose_free,
57: window_choose_resize,
58: window_choose_key,
59: window_choose_mouse,
60: NULL,
61: };
62:
63: struct window_choose_mode_data {
64: struct screen screen;
65:
66: struct mode_key_data mdata;
67:
68: ARRAY_DECL(, struct window_choose_mode_item) list;
1.27 nicm 69: ARRAY_DECL(, struct window_choose_mode_item) old_list;
1.22 nicm 70: int width;
1.1 nicm 71: u_int top;
72: u_int selected;
1.22 nicm 73: enum window_choose_input_type input_type;
74: const char *input_prompt;
75: char *input_str;
1.1 nicm 76:
1.19 nicm 77: void (*callbackfn)(struct window_choose_data *);
78: void (*freefn)(struct window_choose_data *);
1.1 nicm 79: };
80:
1.25 nicm 81: int window_choose_key_index(struct window_choose_mode_data *, u_int);
82: int window_choose_index_key(struct window_choose_mode_data *, int);
1.22 nicm 83: void window_choose_prompt_input(enum window_choose_input_type,
84: const char *, struct window_pane *, int);
1.11 nicm 85:
1.1 nicm 86: void
1.19 nicm 87: window_choose_add(struct window_pane *wp, struct window_choose_data *wcd)
1.1 nicm 88: {
89: struct window_choose_mode_data *data = wp->modedata;
90: struct window_choose_mode_item *item;
1.22 nicm 91: char tmp[10];
1.1 nicm 92:
93: ARRAY_EXPAND(&data->list, 1);
94: item = &ARRAY_LAST(&data->list);
95:
1.19 nicm 96: item->name = format_expand(wcd->ft, wcd->ft_template);
97: item->wcd = wcd;
1.22 nicm 98: item->pos = ARRAY_LENGTH(&data->list) - 1;
1.27 nicm 99: item->state = 0;
1.22 nicm 100:
1.25 nicm 101: data->width = xsnprintf (tmp, sizeof tmp , "%u", item->pos);
1.1 nicm 102: }
103:
104: void
1.3 nicm 105: window_choose_ready(struct window_pane *wp, u_int cur,
1.34 nicm 106: void (*callbackfn)(struct window_choose_data *))
1.1 nicm 107: {
108: struct window_choose_mode_data *data = wp->modedata;
109: struct screen *s = &data->screen;
110:
111: data->selected = cur;
112: if (data->selected > screen_size_y(s) - 1)
113: data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s);
114:
1.3 nicm 115: data->callbackfn = callbackfn;
1.33 nicm 116: if (data->callbackfn == NULL)
117: data->callbackfn = window_choose_default_callback;
1.1 nicm 118:
1.27 nicm 119: ARRAY_CONCAT(&data->old_list, &data->list);
120:
121: window_choose_collapse_all(wp);
1.1 nicm 122: }
123:
124: struct screen *
125: window_choose_init(struct window_pane *wp)
126: {
127: struct window_choose_mode_data *data;
128: struct screen *s;
1.5 nicm 129: int keys;
1.1 nicm 130:
131: wp->modedata = data = xmalloc(sizeof *data);
1.3 nicm 132:
133: data->callbackfn = NULL;
134: data->freefn = NULL;
1.22 nicm 135: data->input_type = WINDOW_CHOOSE_NORMAL;
136: data->input_str = xstrdup("");
137: data->input_prompt = NULL;
1.3 nicm 138:
1.1 nicm 139: ARRAY_INIT(&data->list);
1.27 nicm 140: ARRAY_INIT(&data->old_list);
1.1 nicm 141: data->top = 0;
142:
143: s = &data->screen;
144: screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
145: s->mode &= ~MODE_CURSOR;
1.7 nicm 146: if (options_get_number(&wp->window->options, "mode-mouse"))
1.17 nicm 147: s->mode |= MODE_MOUSE_STANDARD;
1.1 nicm 148:
1.5 nicm 149: keys = options_get_number(&wp->window->options, "mode-keys");
150: if (keys == MODEKEY_EMACS)
1.6 nicm 151: mode_key_init(&data->mdata, &mode_key_tree_emacs_choice);
1.5 nicm 152: else
1.6 nicm 153: mode_key_init(&data->mdata, &mode_key_tree_vi_choice);
1.1 nicm 154:
155: return (s);
156: }
157:
1.19 nicm 158: struct window_choose_data *
1.33 nicm 159: window_choose_data_create(int type, struct client *c, struct session *s)
1.19 nicm 160: {
161: struct window_choose_data *wcd;
162:
163: wcd = xmalloc(sizeof *wcd);
1.33 nicm 164: wcd->type = type;
165:
1.19 nicm 166: wcd->ft = format_create();
167: wcd->ft_template = NULL;
1.33 nicm 168:
1.20 nicm 169: wcd->command = NULL;
1.33 nicm 170:
1.24 nicm 171: wcd->wl = NULL;
1.33 nicm 172: wcd->pane_id = -1;
173: wcd->idx = -1;
174:
1.27 nicm 175: wcd->tree_session = NULL;
1.33 nicm 176:
177: wcd->start_client = c;
178: wcd->start_client->references++;
179: wcd->start_session = s;
180: wcd->start_session->references++;
1.19 nicm 181:
182: return (wcd);
183: }
184:
1.1 nicm 185: void
1.33 nicm 186: window_choose_data_free(struct window_choose_data *wcd)
187: {
188: wcd->start_client->references--;
189: wcd->start_session->references--;
190:
191: if (wcd->tree_session != NULL)
192: wcd->tree_session->references--;
193:
194: free(wcd->ft_template);
195: format_free(wcd->ft);
196:
197: free(wcd->command);
198: free(wcd);
199: }
200:
201: void
202: window_choose_data_run(struct window_choose_data *cdata)
203: {
204: struct cmd_ctx ctx;
205: struct cmd_list *cmdlist;
206: char *cause;
207:
208: /*
209: * The command template will have already been replaced. But if it's
210: * NULL, bail here.
211: */
212: if (cdata->command == NULL)
213: return;
214:
215: if (cmd_string_parse(cdata->command, &cmdlist, &cause) != 0) {
216: if (cause != NULL) {
217: *cause = toupper((u_char) *cause);
218: status_message_set(cdata->start_client, "%s", cause);
219: free(cause);
220: }
221: return;
222: }
223:
224: ctx.msgdata = NULL;
225: ctx.curclient = cdata->start_client;
226:
227: ctx.error = key_bindings_error;
228: ctx.print = key_bindings_print;
229: ctx.info = key_bindings_info;
230:
231: ctx.cmdclient = NULL;
232:
233: cmd_list_exec(cmdlist, &ctx);
234: cmd_list_free(cmdlist);
235: }
236:
237: void
238: window_choose_default_callback(struct window_choose_data *wcd)
239: {
240: if (wcd == NULL)
241: return;
242: if (wcd->start_client->flags & CLIENT_DEAD)
243: return;
244:
245: window_choose_data_run(wcd);
246: }
247:
248: void
1.1 nicm 249: window_choose_free(struct window_pane *wp)
250: {
251: struct window_choose_mode_data *data = wp->modedata;
1.19 nicm 252: struct window_choose_mode_item *item;
1.1 nicm 253: u_int i;
254:
1.28 nicm 255: for (i = 0; i < ARRAY_LENGTH(&data->old_list); i++) {
256: item = &ARRAY_ITEM(&data->old_list, i);
1.33 nicm 257: window_choose_data_free(item->wcd);
1.21 nicm 258: free(item->name);
1.19 nicm 259: }
1.1 nicm 260: ARRAY_FREE(&data->list);
1.27 nicm 261: ARRAY_FREE(&data->old_list);
1.22 nicm 262: free(data->input_str);
1.1 nicm 263:
264: screen_free(&data->screen);
1.21 nicm 265: free(data);
1.1 nicm 266: }
267:
268: void
269: window_choose_resize(struct window_pane *wp, u_int sx, u_int sy)
270: {
271: struct window_choose_mode_data *data = wp->modedata;
272: struct screen *s = &data->screen;
273:
274: data->top = 0;
275: if (data->selected > sy - 1)
276: data->top = data->selected - (sy - 1);
277:
1.32 nicm 278: screen_resize(s, sx, sy, 0);
1.1 nicm 279: window_choose_redraw_screen(wp);
280: }
281:
1.18 nicm 282: void
1.19 nicm 283: window_choose_fire_callback(
1.33 nicm 284: struct window_pane *wp, struct window_choose_data *wcd)
1.18 nicm 285: {
286: struct window_choose_mode_data *data = wp->modedata;
287: const struct window_mode *oldmode;
288:
289: oldmode = wp->mode;
290: wp->mode = NULL;
291:
1.19 nicm 292: data->callbackfn(wcd);
1.18 nicm 293:
294: wp->mode = oldmode;
295: }
296:
1.22 nicm 297: void
298: window_choose_prompt_input(enum window_choose_input_type input_type,
299: const char *prompt, struct window_pane *wp, int key)
300: {
301: struct window_choose_mode_data *data = wp->modedata;
302: size_t input_len;
303:
304: data->input_type = input_type;
305: data->input_prompt = prompt;
306: input_len = strlen(data->input_str) + 2;
307:
308: data->input_str = xrealloc(data->input_str, 1, input_len);
309: data->input_str[input_len - 2] = key;
310: data->input_str[input_len - 1] = '\0';
311:
312: window_choose_redraw_screen(wp);
313: }
314:
1.27 nicm 315: void
316: window_choose_collapse(struct window_pane *wp, struct session *s)
317: {
318: struct window_choose_mode_data *data = wp->modedata;
319: struct window_choose_mode_item *item, *chosen;
320: struct window_choose_data *wcd;
321: u_int i, pos;
322:
323: ARRAY_DECL(, struct window_choose_mode_item) list_copy;
324: ARRAY_INIT(&list_copy);
325:
326: pos = data->selected;
327:
328: chosen = &ARRAY_ITEM(&data->list, pos);
329: chosen->state &= ~TREE_EXPANDED;
330:
331: /*
332: * Trying to mangle the &data->list in-place has lots of problems, so
333: * assign the actual result we want to render and copy the new one over
334: * the top of it.
335: */
336: for (i = 0; i < ARRAY_LENGTH(&data->list); i++)
337: {
338: item = &ARRAY_ITEM(&data->list, i);
339: wcd = item->wcd;
340:
341: if (s == wcd->tree_session) {
342: /* We only show the session when collapsed. */
343: if (wcd->type & TREE_SESSION) {
344: item->state &= ~TREE_EXPANDED;
345:
346: ARRAY_ADD(&list_copy,
347: ARRAY_ITEM(&data->list, i));
348: /*
349: * Update the selection to this session item so
350: * we don't end up highlighting a non-existent
351: * item.
352: */
353: data->selected = i;
354: }
355: } else
356: ARRAY_ADD(&list_copy, ARRAY_ITEM(&data->list, i));
357: }
358:
359: if (!ARRAY_EMPTY(&list_copy)) {
360: ARRAY_FREE(&data->list);
361: ARRAY_CONCAT(&data->list, &list_copy);
1.28 nicm 362: ARRAY_FREE(&list_copy);
1.27 nicm 363: }
364: }
365:
366: void
367: window_choose_collapse_all(struct window_pane *wp)
368: {
369: struct window_choose_mode_data *data = wp->modedata;
1.28 nicm 370: struct window_choose_mode_item *item;
371: struct session *s, *chosen;
1.27 nicm 372: u_int i;
373:
1.33 nicm 374: chosen = ARRAY_ITEM(&data->list, data->selected).wcd->start_session;
1.27 nicm 375:
376: RB_FOREACH(s, sessions, &sessions)
377: window_choose_collapse(wp, s);
378:
379: /* Reset the selection back to the starting session. */
380: for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
381: item = &ARRAY_ITEM(&data->list, i);
382:
1.28 nicm 383: if (chosen != item->wcd->tree_session)
1.27 nicm 384: continue;
385:
386: if (item->wcd->type & TREE_SESSION)
387: data->selected = i;
388: }
389: window_choose_redraw_screen(wp);
390: }
391:
392: void
393: window_choose_expand_all(struct window_pane *wp)
394: {
395: struct window_choose_mode_data *data = wp->modedata;
396: struct window_choose_mode_item *item;
397: struct session *s;
398: u_int i;
399:
400: RB_FOREACH(s, sessions, &sessions) {
401: for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
402: item = &ARRAY_ITEM(&data->list, i);
403:
404: if (s != item->wcd->tree_session)
405: continue;
406:
407: if (item->wcd->type & TREE_SESSION)
408: window_choose_expand(wp, s, i);
409: }
410: }
411:
412: window_choose_redraw_screen(wp);
413: }
414:
415: void
416: window_choose_expand(struct window_pane *wp, struct session *s, u_int pos)
417: {
418: struct window_choose_mode_data *data = wp->modedata;
419: struct window_choose_mode_item *item, *chosen;
420: struct window_choose_data *wcd;
421: u_int i, items;
422:
423: chosen = &ARRAY_ITEM(&data->list, pos);
424: items = ARRAY_LENGTH(&data->old_list) - 1;
425:
426: /* It's not possible to expand anything other than sessions. */
427: if (!(chosen->wcd->type & TREE_SESSION))
428: return;
429:
430: /* Don't re-expand a session which is already expanded. */
431: if (chosen->state & TREE_EXPANDED)
432: return;
433:
434: /* Mark the session entry as expanded. */
435: chosen->state |= TREE_EXPANDED;
436:
437: /*
438: * Go back through the original list of all sessions and windows, and
439: * pull out the windows where the session matches the selection chosen
440: * to expand.
441: */
442: for (i = items; i > 0; i--) {
443: item = &ARRAY_ITEM(&data->old_list, i);
444: item->state |= TREE_EXPANDED;
445: wcd = item->wcd;
446:
447: if (s == wcd->tree_session) {
448: /*
449: * Since the session is already displayed, we only care
450: * to add back in window for it.
451: */
452: if (wcd->type & TREE_WINDOW) {
453: /*
454: * If the insertion point for adding the
455: * windows to the session falls inside the
456: * range of the list, then we insert these
457: * entries in order *AFTER* the selected
458: * session.
459: */
460: if (pos < i ) {
461: ARRAY_INSERT(&data->list,
462: pos + 1,
463: ARRAY_ITEM(&data->old_list,
464: i));
465: } else {
466: /* Ran out of room, add to the end. */
467: ARRAY_ADD(&data->list,
468: ARRAY_ITEM(&data->old_list,
469: i));
470: }
471: }
472: }
473: }
474: }
475:
1.12 nicm 476: /* ARGSUSED */
1.1 nicm 477: void
1.16 nicm 478: window_choose_key(struct window_pane *wp, unused struct session *sess, int key)
1.1 nicm 479: {
480: struct window_choose_mode_data *data = wp->modedata;
481: struct screen *s = &data->screen;
482: struct screen_write_ctx ctx;
483: struct window_choose_mode_item *item;
1.22 nicm 484: size_t input_len;
1.27 nicm 485: u_int items, n;
1.11 nicm 486: int idx;
1.1 nicm 487:
488: items = ARRAY_LENGTH(&data->list);
489:
1.25 nicm 490: if (data->input_type == WINDOW_CHOOSE_GOTO_ITEM) {
491: switch (mode_key_lookup(&data->mdata, key)) {
492: case MODEKEYCHOICE_CANCEL:
493: data->input_type = WINDOW_CHOOSE_NORMAL;
494: window_choose_redraw_screen(wp);
1.22 nicm 495: break;
1.25 nicm 496: case MODEKEYCHOICE_CHOOSE:
1.22 nicm 497: n = strtonum(data->input_str, 0, INT_MAX, NULL);
1.25 nicm 498: if (n > items - 1) {
499: data->input_type = WINDOW_CHOOSE_NORMAL;
500: window_choose_redraw_screen(wp);
1.22 nicm 501: break;
1.25 nicm 502: }
1.22 nicm 503: item = &ARRAY_ITEM(&data->list, n);
504: window_choose_fire_callback(wp, item->wcd);
505: window_pane_reset_mode(wp);
506: break;
1.25 nicm 507: case MODEKEYCHOICE_BACKSPACE:
508: input_len = strlen(data->input_str);
509: if (input_len > 0)
510: data->input_str[input_len - 1] = '\0';
511: window_choose_redraw_screen(wp);
512: break;
513: default:
514: if (key < '0' || key > '9')
515: break;
516: window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
517: "Goto Item", wp, key);
518: break;
1.22 nicm 519: }
1.25 nicm 520: return;
521: }
522:
523: switch (mode_key_lookup(&data->mdata, key)) {
524: case MODEKEYCHOICE_CANCEL:
525: window_choose_fire_callback(wp, NULL);
526: window_pane_reset_mode(wp);
527: break;
528: case MODEKEYCHOICE_CHOOSE:
529: item = &ARRAY_ITEM(&data->list, data->selected);
530: window_choose_fire_callback(wp, item->wcd);
531: window_pane_reset_mode(wp);
1.1 nicm 532: break;
1.27 nicm 533: case MODEKEYCHOICE_TREE_TOGGLE:
534: item = &ARRAY_ITEM(&data->list, data->selected);
535: if (item->state & TREE_EXPANDED)
536: window_choose_collapse(wp, item->wcd->tree_session);
537: else {
538: window_choose_expand(wp, item->wcd->tree_session,
539: data->selected);
540: }
541: window_choose_redraw_screen(wp);
542: break;
543: case MODEKEYCHOICE_TREE_COLLAPSE:
544: item = &ARRAY_ITEM(&data->list, data->selected);
545: if (item->state & TREE_EXPANDED) {
546: window_choose_collapse(wp, item->wcd->tree_session);
547: window_choose_redraw_screen(wp);
548: }
549: break;
550: case MODEKEYCHOICE_TREE_COLLAPSE_ALL:
551: window_choose_collapse_all(wp);
552: break;
553: case MODEKEYCHOICE_TREE_EXPAND:
554: item = &ARRAY_ITEM(&data->list, data->selected);
555: if (!(item->state & TREE_EXPANDED)) {
556: window_choose_expand(wp, item->wcd->tree_session,
557: data->selected);
558: window_choose_redraw_screen(wp);
559: }
560: break;
561: case MODEKEYCHOICE_TREE_EXPAND_ALL:
562: window_choose_expand_all(wp);
563: break;
1.5 nicm 564: case MODEKEYCHOICE_UP:
1.1 nicm 565: if (items == 0)
566: break;
567: if (data->selected == 0) {
568: data->selected = items - 1;
569: if (data->selected > screen_size_y(s) - 1)
570: data->top = items - screen_size_y(s);
571: window_choose_redraw_screen(wp);
572: break;
573: }
574: data->selected--;
575: if (data->selected < data->top)
576: window_choose_scroll_up(wp);
577: else {
578: screen_write_start(&ctx, wp, NULL);
579: window_choose_write_line(
580: wp, &ctx, data->selected - data->top);
581: window_choose_write_line(
582: wp, &ctx, data->selected + 1 - data->top);
583: screen_write_stop(&ctx);
584: }
585: break;
1.5 nicm 586: case MODEKEYCHOICE_DOWN:
1.1 nicm 587: if (items == 0)
588: break;
589: if (data->selected == items - 1) {
590: data->selected = 0;
591: data->top = 0;
592: window_choose_redraw_screen(wp);
593: break;
594: }
595: data->selected++;
1.5 nicm 596:
1.15 nicm 597: if (data->selected < data->top + screen_size_y(s)) {
1.1 nicm 598: screen_write_start(&ctx, wp, NULL);
599: window_choose_write_line(
600: wp, &ctx, data->selected - data->top);
601: window_choose_write_line(
602: wp, &ctx, data->selected - 1 - data->top);
603: screen_write_stop(&ctx);
1.15 nicm 604: } else
605: window_choose_scroll_down(wp);
606: break;
607: case MODEKEYCHOICE_SCROLLUP:
608: if (items == 0 || data->top == 0)
609: break;
610: if (data->selected == data->top + screen_size_y(s) - 1) {
611: data->selected--;
612: window_choose_scroll_up(wp);
613: screen_write_start(&ctx, wp, NULL);
614: window_choose_write_line(
615: wp, &ctx, screen_size_y(s) - 1);
616: screen_write_stop(&ctx);
617: } else
618: window_choose_scroll_up(wp);
619: break;
620: case MODEKEYCHOICE_SCROLLDOWN:
621: if (items == 0 ||
622: data->top + screen_size_y(&data->screen) >= items)
623: break;
624: if (data->selected == data->top) {
625: data->selected++;
626: window_choose_scroll_down(wp);
627: screen_write_start(&ctx, wp, NULL);
628: window_choose_write_line(wp, &ctx, 0);
629: screen_write_stop(&ctx);
630: } else
631: window_choose_scroll_down(wp);
1.1 nicm 632: break;
1.5 nicm 633: case MODEKEYCHOICE_PAGEUP:
1.1 nicm 634: if (data->selected < screen_size_y(s)) {
635: data->selected = 0;
636: data->top = 0;
637: } else {
638: data->selected -= screen_size_y(s);
639: if (data->top < screen_size_y(s))
640: data->top = 0;
641: else
642: data->top -= screen_size_y(s);
643: }
1.13 nicm 644: window_choose_redraw_screen(wp);
1.1 nicm 645: break;
1.5 nicm 646: case MODEKEYCHOICE_PAGEDOWN:
1.1 nicm 647: data->selected += screen_size_y(s);
648: if (data->selected > items - 1)
649: data->selected = items - 1;
650: data->top += screen_size_y(s);
651: if (screen_size_y(s) < items) {
652: if (data->top + screen_size_y(s) > items)
653: data->top = items - screen_size_y(s);
654: } else
655: data->top = 0;
656: if (data->selected < data->top)
657: data->top = data->selected;
658: window_choose_redraw_screen(wp);
659: break;
1.22 nicm 660: case MODEKEYCHOICE_BACKSPACE:
661: input_len = strlen(data->input_str);
662: if (input_len > 0)
663: data->input_str[input_len - 1] = '\0';
664: window_choose_redraw_screen(wp);
665: break;
666: case MODEKEYCHOICE_STARTNUMBERPREFIX:
1.25 nicm 667: key &= KEYC_MASK_KEY;
668: if (key < '0' || key > '9')
1.11 nicm 669: break;
1.25 nicm 670: window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
671: "Goto Item", wp, key);
1.22 nicm 672: break;
673: default:
1.25 nicm 674: idx = window_choose_index_key(data, key);
675: if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list))
676: break;
677: data->selected = idx;
678:
679: item = &ARRAY_ITEM(&data->list, data->selected);
680: window_choose_fire_callback(wp, item->wcd);
681: window_pane_reset_mode(wp);
1.1 nicm 682: break;
683: }
684: }
685:
1.12 nicm 686: /* ARGSUSED */
1.1 nicm 687: void
1.10 nicm 688: window_choose_mouse(
1.16 nicm 689: struct window_pane *wp, unused struct session *sess, struct mouse_event *m)
1.1 nicm 690: {
691: struct window_choose_mode_data *data = wp->modedata;
692: struct screen *s = &data->screen;
693: struct window_choose_mode_item *item;
694: u_int idx;
695:
1.29 nicm 696: if (~m->event & MOUSE_EVENT_CLICK)
1.1 nicm 697: return;
1.10 nicm 698: if (m->x >= screen_size_x(s))
1.1 nicm 699: return;
1.10 nicm 700: if (m->y >= screen_size_y(s))
1.1 nicm 701: return;
702:
1.10 nicm 703: idx = data->top + m->y;
1.1 nicm 704: if (idx >= ARRAY_LENGTH(&data->list))
705: return;
706: data->selected = idx;
707:
708: item = &ARRAY_ITEM(&data->list, data->selected);
1.19 nicm 709: window_choose_fire_callback(wp, item->wcd);
1.1 nicm 710: window_pane_reset_mode(wp);
711: }
712:
713: void
714: window_choose_write_line(
715: struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
716: {
717: struct window_choose_mode_data *data = wp->modedata;
718: struct window_choose_mode_item *item;
1.9 nicm 719: struct options *oo = &wp->window->options;
1.1 nicm 720: struct screen *s = &data->screen;
721: struct grid_cell gc;
1.22 nicm 722: size_t last, xoff = 0;
1.25 nicm 723: char hdr[32], label[32];
724: int utf8flag, key;
1.1 nicm 725:
1.3 nicm 726: if (data->callbackfn == NULL)
1.1 nicm 727: fatalx("called before callback assigned");
728:
1.22 nicm 729: last = screen_size_y(s) - 1;
1.2 nicm 730: utf8flag = options_get_number(&wp->window->options, "utf8");
1.1 nicm 731: memcpy(&gc, &grid_default_cell, sizeof gc);
1.22 nicm 732: if (data->selected == data->top + py)
733: window_mode_attrs(&gc, oo);
1.1 nicm 734:
735: screen_write_cursormove(ctx, 0, py);
736: if (data->top + py < ARRAY_LENGTH(&data->list)) {
737: item = &ARRAY_ITEM(&data->list, data->top + py);
1.24 nicm 738: if (item->wcd->wl != NULL &&
739: item->wcd->wl->flags & WINLINK_ALERTFLAGS)
740: gc.attr |= GRID_ATTR_BRIGHT;
1.11 nicm 741:
1.25 nicm 742: key = window_choose_key_index(data, data->top + py);
743: if (key != -1)
744: xsnprintf (label, sizeof label, "(%c)", key);
745: else
746: xsnprintf (label, sizeof label, "(%d)", item->pos);
747: screen_write_nputs(ctx, screen_size_x(s) - 1, &gc, utf8flag,
1.27 nicm 748: "%*s %s %s", data->width + 2, label,
749: /*
750: * Add indication to tree if necessary about whether it's
751: * expanded or not.
752: */
753: (item->wcd->type & TREE_SESSION) ?
754: (item->state & TREE_EXPANDED ? "-" : "+") : "", item->name);
1.1 nicm 755: }
756: while (s->cx < screen_size_x(s))
757: screen_write_putc(ctx, &gc, ' ');
1.11 nicm 758:
1.22 nicm 759: if (data->input_type != WINDOW_CHOOSE_NORMAL) {
760: window_mode_attrs(&gc, oo);
1.11 nicm 761:
1.22 nicm 762: xoff = xsnprintf(hdr, sizeof hdr,
763: "%s: %s", data->input_prompt, data->input_str);
764: screen_write_cursormove(ctx, 0, last);
765: screen_write_puts(ctx, &gc, "%s", hdr);
766: screen_write_cursormove(ctx, xoff, py);
767: memcpy(&gc, &grid_default_cell, sizeof gc);
1.11 nicm 768: }
1.22 nicm 769:
1.11 nicm 770: }
771:
772: int
1.25 nicm 773: window_choose_key_index(struct window_choose_mode_data *data, u_int idx)
774: {
775: static const char keys[] = "0123456789"
776: "abcdefghijklmnopqrstuvwxyz"
777: "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
778: const char *ptr;
779: int mkey;
780:
781: for (ptr = keys; *ptr != '\0'; ptr++) {
782: mkey = mode_key_lookup(&data->mdata, *ptr);
783: if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER)
784: continue;
785: if (idx-- == 0)
786: return (*ptr);
787: }
788: return (-1);
789: }
790:
791: int
792: window_choose_index_key(struct window_choose_mode_data *data, int key)
1.11 nicm 793: {
1.25 nicm 794: static const char keys[] = "0123456789"
795: "abcdefghijklmnopqrstuvwxyz"
796: "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1.11 nicm 797: const char *ptr;
1.25 nicm 798: int mkey;
1.11 nicm 799: u_int idx = 0;
800:
801: for (ptr = keys; *ptr != '\0'; ptr++) {
1.25 nicm 802: mkey = mode_key_lookup(&data->mdata, *ptr);
803: if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER)
804: continue;
1.11 nicm 805: if (key == *ptr)
806: return (idx);
807: idx++;
808: }
809: return (-1);
1.1 nicm 810: }
811:
812: void
813: window_choose_redraw_screen(struct window_pane *wp)
814: {
815: struct window_choose_mode_data *data = wp->modedata;
816: struct screen *s = &data->screen;
817: struct screen_write_ctx ctx;
818: u_int i;
819:
820: screen_write_start(&ctx, wp, NULL);
821: for (i = 0; i < screen_size_y(s); i++)
822: window_choose_write_line(wp, &ctx, i);
823: screen_write_stop(&ctx);
824: }
825:
826: void
827: window_choose_scroll_up(struct window_pane *wp)
828: {
829: struct window_choose_mode_data *data = wp->modedata;
830: struct screen_write_ctx ctx;
831:
832: if (data->top == 0)
833: return;
834: data->top--;
835:
836: screen_write_start(&ctx, wp, NULL);
837: screen_write_cursormove(&ctx, 0, 0);
838: screen_write_insertline(&ctx, 1);
839: window_choose_write_line(wp, &ctx, 0);
840: if (screen_size_y(&data->screen) > 1)
841: window_choose_write_line(wp, &ctx, 1);
842: screen_write_stop(&ctx);
843: }
844:
845: void
846: window_choose_scroll_down(struct window_pane *wp)
847: {
848: struct window_choose_mode_data *data = wp->modedata;
849: struct screen *s = &data->screen;
850: struct screen_write_ctx ctx;
851:
852: if (data->top >= ARRAY_LENGTH(&data->list))
853: return;
854: data->top++;
855:
856: screen_write_start(&ctx, wp, NULL);
857: screen_write_cursormove(&ctx, 0, 0);
858: screen_write_deleteline(&ctx, 1);
859: window_choose_write_line(wp, &ctx, screen_size_y(s) - 1);
860: if (screen_size_y(&data->screen) > 1)
861: window_choose_write_line(wp, &ctx, screen_size_y(s) - 2);
862: screen_write_stop(&ctx);
1.19 nicm 863: }
864:
1.20 nicm 865: struct window_choose_data *
1.35 nicm 866: window_choose_add_session(struct window_pane *wp, struct client *c,
1.36 ! nicm 867: struct session *s, const char *template, const char *action, u_int idx)
1.20 nicm 868: {
869: struct window_choose_data *wcd;
870:
1.33 nicm 871: wcd = window_choose_data_create(TREE_SESSION, c, c->session);
1.20 nicm 872: wcd->idx = s->idx;
1.33 nicm 873:
1.27 nicm 874: wcd->tree_session = s;
1.33 nicm 875: wcd->tree_session->references++;
876:
1.20 nicm 877: wcd->ft_template = xstrdup(template);
878: format_add(wcd->ft, "line", "%u", idx);
879: format_session(wcd->ft, s);
880:
1.33 nicm 881: wcd->command = cmd_template_replace(action, s->name, 1);
1.20 nicm 882:
883: window_choose_add(wp, wcd);
884:
885: return (wcd);
1.26 nicm 886: }
887:
888: struct window_choose_data *
1.35 nicm 889: window_choose_add_item(struct window_pane *wp, struct client *c,
1.36 ! nicm 890: struct winlink *wl, const char *template, const char *action, u_int idx)
1.26 nicm 891: {
892: struct window_choose_data *wcd;
1.33 nicm 893: char *expanded;
1.26 nicm 894:
1.33 nicm 895: wcd = window_choose_data_create(TREE_OTHER, c, c->session);
1.26 nicm 896: wcd->idx = wl->idx;
1.33 nicm 897:
1.26 nicm 898: wcd->ft_template = xstrdup(template);
899: format_add(wcd->ft, "line", "%u", idx);
1.33 nicm 900: format_session(wcd->ft, wcd->start_session);
901: format_winlink(wcd->ft, wcd->start_session, wl);
1.26 nicm 902: format_window_pane(wcd->ft, wl->window->active);
903:
1.33 nicm 904: /*
905: * Interpolate action here, since the data we pass back is the expanded
906: * template itself.
907: */
908: xasprintf(&expanded, "%s", format_expand(wcd->ft, wcd->ft_template));
909: wcd->command = cmd_template_replace(action, expanded, 1);
910: free(expanded);
1.26 nicm 911:
912: window_choose_add(wp, wcd);
913:
914: return (wcd);
915:
1.20 nicm 916: }
917:
918: struct window_choose_data *
1.35 nicm 919: window_choose_add_window(struct window_pane *wp, struct client *c,
1.22 nicm 920: struct session *s, struct winlink *wl, const char *template,
1.36 ! nicm 921: const char *action, u_int idx)
1.20 nicm 922: {
923: struct window_choose_data *wcd;
1.33 nicm 924: char *expanded;
1.20 nicm 925:
1.33 nicm 926: wcd = window_choose_data_create(TREE_WINDOW, c, c->session);
927: wcd->idx = wl->idx;
1.20 nicm 928:
1.33 nicm 929: wcd->wl = wl;
1.20 nicm 930:
1.27 nicm 931: wcd->tree_session = s;
1.33 nicm 932: wcd->tree_session->references++;
933:
1.20 nicm 934: wcd->ft_template = xstrdup(template);
935: format_add(wcd->ft, "line", "%u", idx);
936: format_session(wcd->ft, s);
937: format_winlink(wcd->ft, s, wl);
938: format_window_pane(wcd->ft, wl->window->active);
939:
1.33 nicm 940: xasprintf(&expanded, "%s:%d", s->name, wl->idx);
941: wcd->command = cmd_template_replace(action, expanded, 1);
942: free(expanded);
1.20 nicm 943:
944: window_choose_add(wp, wcd);
945:
946: return (wcd);
1.1 nicm 947: }