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