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