Annotation of src/usr.bin/tmux/window-choose.c, Revision 1.39
1.39 ! nicm 1: /* $OpenBSD: window-choose.c,v 1.38 2013/03/21 16:22:48 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.38 nicm 80: void window_choose_free1(struct window_choose_mode_data *);
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;
1.22 nicm 134: data->input_type = WINDOW_CHOOSE_NORMAL;
135: data->input_str = xstrdup("");
136: data->input_prompt = NULL;
1.3 nicm 137:
1.1 nicm 138: ARRAY_INIT(&data->list);
1.27 nicm 139: ARRAY_INIT(&data->old_list);
1.1 nicm 140: data->top = 0;
141:
142: s = &data->screen;
143: screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
144: s->mode &= ~MODE_CURSOR;
1.7 nicm 145: if (options_get_number(&wp->window->options, "mode-mouse"))
1.17 nicm 146: s->mode |= MODE_MOUSE_STANDARD;
1.1 nicm 147:
1.5 nicm 148: keys = options_get_number(&wp->window->options, "mode-keys");
149: if (keys == MODEKEY_EMACS)
1.6 nicm 150: mode_key_init(&data->mdata, &mode_key_tree_emacs_choice);
1.5 nicm 151: else
1.6 nicm 152: mode_key_init(&data->mdata, &mode_key_tree_vi_choice);
1.1 nicm 153:
154: return (s);
155: }
156:
1.19 nicm 157: struct window_choose_data *
1.33 nicm 158: window_choose_data_create(int type, struct client *c, struct session *s)
1.19 nicm 159: {
160: struct window_choose_data *wcd;
161:
162: wcd = xmalloc(sizeof *wcd);
1.33 nicm 163: wcd->type = type;
164:
1.19 nicm 165: wcd->ft = format_create();
166: wcd->ft_template = NULL;
1.33 nicm 167:
1.20 nicm 168: wcd->command = NULL;
1.33 nicm 169:
1.24 nicm 170: wcd->wl = NULL;
1.33 nicm 171: wcd->pane_id = -1;
172: wcd->idx = -1;
173:
1.27 nicm 174: wcd->tree_session = NULL;
1.33 nicm 175:
176: wcd->start_client = c;
177: wcd->start_client->references++;
178: wcd->start_session = s;
179: wcd->start_session->references++;
1.19 nicm 180:
181: return (wcd);
182: }
183:
1.1 nicm 184: void
1.33 nicm 185: window_choose_data_free(struct window_choose_data *wcd)
186: {
187: wcd->start_client->references--;
188: wcd->start_session->references--;
189:
190: if (wcd->tree_session != NULL)
191: wcd->tree_session->references--;
192:
193: free(wcd->ft_template);
194: format_free(wcd->ft);
195:
196: free(wcd->command);
197: free(wcd);
198: }
199:
200: void
201: window_choose_data_run(struct window_choose_data *cdata)
202: {
203: struct cmd_ctx ctx;
204: struct cmd_list *cmdlist;
205: char *cause;
206:
207: /*
208: * The command template will have already been replaced. But if it's
209: * NULL, bail here.
210: */
211: if (cdata->command == NULL)
212: return;
213:
214: if (cmd_string_parse(cdata->command, &cmdlist, &cause) != 0) {
215: if (cause != NULL) {
216: *cause = toupper((u_char) *cause);
217: status_message_set(cdata->start_client, "%s", cause);
218: free(cause);
219: }
220: return;
221: }
222:
223: ctx.msgdata = NULL;
224: ctx.curclient = cdata->start_client;
225:
226: ctx.error = key_bindings_error;
227: ctx.print = key_bindings_print;
228: ctx.info = key_bindings_info;
229:
230: ctx.cmdclient = NULL;
231:
232: cmd_list_exec(cmdlist, &ctx);
233: cmd_list_free(cmdlist);
234: }
235:
236: void
237: window_choose_default_callback(struct window_choose_data *wcd)
238: {
239: if (wcd == NULL)
240: return;
241: if (wcd->start_client->flags & CLIENT_DEAD)
242: return;
243:
244: window_choose_data_run(wcd);
245: }
246:
247: void
1.1 nicm 248: window_choose_free(struct window_pane *wp)
249: {
1.38 nicm 250: if (wp->modedata != NULL)
251: window_choose_free1(wp->modedata);
252: }
253:
254: void
255: window_choose_free1(struct window_choose_mode_data *data)
256: {
1.19 nicm 257: struct window_choose_mode_item *item;
1.1 nicm 258: u_int i;
259:
1.38 nicm 260: if (data == NULL)
261: return;
262:
1.28 nicm 263: for (i = 0; i < ARRAY_LENGTH(&data->old_list); i++) {
264: item = &ARRAY_ITEM(&data->old_list, i);
1.33 nicm 265: window_choose_data_free(item->wcd);
1.21 nicm 266: free(item->name);
1.19 nicm 267: }
1.1 nicm 268: ARRAY_FREE(&data->list);
1.27 nicm 269: ARRAY_FREE(&data->old_list);
1.22 nicm 270: free(data->input_str);
1.1 nicm 271:
272: screen_free(&data->screen);
1.21 nicm 273: free(data);
1.1 nicm 274: }
275:
276: void
277: window_choose_resize(struct window_pane *wp, u_int sx, u_int sy)
278: {
279: struct window_choose_mode_data *data = wp->modedata;
280: struct screen *s = &data->screen;
281:
282: data->top = 0;
283: if (data->selected > sy - 1)
284: data->top = data->selected - (sy - 1);
285:
1.32 nicm 286: screen_resize(s, sx, sy, 0);
1.1 nicm 287: window_choose_redraw_screen(wp);
288: }
289:
1.18 nicm 290: void
1.19 nicm 291: window_choose_fire_callback(
1.33 nicm 292: struct window_pane *wp, struct window_choose_data *wcd)
1.18 nicm 293: {
294: struct window_choose_mode_data *data = wp->modedata;
295:
1.38 nicm 296: wp->modedata = NULL;
297: window_pane_reset_mode(wp);
1.18 nicm 298:
1.19 nicm 299: data->callbackfn(wcd);
1.18 nicm 300:
1.38 nicm 301: window_choose_free1(data);
1.18 nicm 302: }
303:
1.22 nicm 304: void
305: window_choose_prompt_input(enum window_choose_input_type input_type,
306: const char *prompt, struct window_pane *wp, int key)
307: {
308: struct window_choose_mode_data *data = wp->modedata;
309: size_t input_len;
310:
311: data->input_type = input_type;
312: data->input_prompt = prompt;
313: input_len = strlen(data->input_str) + 2;
314:
315: data->input_str = xrealloc(data->input_str, 1, input_len);
316: data->input_str[input_len - 2] = key;
317: data->input_str[input_len - 1] = '\0';
318:
319: window_choose_redraw_screen(wp);
320: }
321:
1.27 nicm 322: void
323: window_choose_collapse(struct window_pane *wp, struct session *s)
324: {
325: struct window_choose_mode_data *data = wp->modedata;
326: struct window_choose_mode_item *item, *chosen;
327: struct window_choose_data *wcd;
328: u_int i, pos;
329:
330: ARRAY_DECL(, struct window_choose_mode_item) list_copy;
331: ARRAY_INIT(&list_copy);
332:
333: pos = data->selected;
334:
335: chosen = &ARRAY_ITEM(&data->list, pos);
336: chosen->state &= ~TREE_EXPANDED;
337:
338: /*
339: * Trying to mangle the &data->list in-place has lots of problems, so
340: * assign the actual result we want to render and copy the new one over
341: * the top of it.
342: */
343: for (i = 0; i < ARRAY_LENGTH(&data->list); i++)
344: {
345: item = &ARRAY_ITEM(&data->list, i);
346: wcd = item->wcd;
347:
348: if (s == wcd->tree_session) {
349: /* We only show the session when collapsed. */
350: if (wcd->type & TREE_SESSION) {
351: item->state &= ~TREE_EXPANDED;
352:
353: ARRAY_ADD(&list_copy,
354: ARRAY_ITEM(&data->list, i));
355: /*
356: * Update the selection to this session item so
357: * we don't end up highlighting a non-existent
358: * item.
359: */
360: data->selected = i;
361: }
362: } else
363: ARRAY_ADD(&list_copy, ARRAY_ITEM(&data->list, i));
364: }
365:
366: if (!ARRAY_EMPTY(&list_copy)) {
367: ARRAY_FREE(&data->list);
368: ARRAY_CONCAT(&data->list, &list_copy);
1.28 nicm 369: ARRAY_FREE(&list_copy);
1.27 nicm 370: }
371: }
372:
373: void
374: window_choose_collapse_all(struct window_pane *wp)
375: {
376: struct window_choose_mode_data *data = wp->modedata;
1.28 nicm 377: struct window_choose_mode_item *item;
378: struct session *s, *chosen;
1.27 nicm 379: u_int i;
380:
1.33 nicm 381: chosen = ARRAY_ITEM(&data->list, data->selected).wcd->start_session;
1.27 nicm 382:
383: RB_FOREACH(s, sessions, &sessions)
384: window_choose_collapse(wp, s);
385:
386: /* Reset the selection back to the starting session. */
387: for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
388: item = &ARRAY_ITEM(&data->list, i);
389:
1.28 nicm 390: if (chosen != item->wcd->tree_session)
1.27 nicm 391: continue;
392:
393: if (item->wcd->type & TREE_SESSION)
394: data->selected = i;
395: }
396: window_choose_redraw_screen(wp);
397: }
398:
399: void
400: window_choose_expand_all(struct window_pane *wp)
401: {
402: struct window_choose_mode_data *data = wp->modedata;
403: struct window_choose_mode_item *item;
404: struct session *s;
405: u_int i;
406:
407: RB_FOREACH(s, sessions, &sessions) {
408: for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
409: item = &ARRAY_ITEM(&data->list, i);
410:
411: if (s != item->wcd->tree_session)
412: continue;
413:
414: if (item->wcd->type & TREE_SESSION)
415: window_choose_expand(wp, s, i);
416: }
417: }
418:
419: window_choose_redraw_screen(wp);
420: }
421:
422: void
423: window_choose_expand(struct window_pane *wp, struct session *s, u_int pos)
424: {
425: struct window_choose_mode_data *data = wp->modedata;
426: struct window_choose_mode_item *item, *chosen;
427: struct window_choose_data *wcd;
428: u_int i, items;
429:
430: chosen = &ARRAY_ITEM(&data->list, pos);
431: items = ARRAY_LENGTH(&data->old_list) - 1;
432:
433: /* It's not possible to expand anything other than sessions. */
434: if (!(chosen->wcd->type & TREE_SESSION))
435: return;
436:
437: /* Don't re-expand a session which is already expanded. */
438: if (chosen->state & TREE_EXPANDED)
439: return;
440:
441: /* Mark the session entry as expanded. */
442: chosen->state |= TREE_EXPANDED;
443:
444: /*
445: * Go back through the original list of all sessions and windows, and
446: * pull out the windows where the session matches the selection chosen
447: * to expand.
448: */
449: for (i = items; i > 0; i--) {
450: item = &ARRAY_ITEM(&data->old_list, i);
451: item->state |= TREE_EXPANDED;
452: wcd = item->wcd;
453:
454: if (s == wcd->tree_session) {
455: /*
456: * Since the session is already displayed, we only care
457: * to add back in window for it.
458: */
459: if (wcd->type & TREE_WINDOW) {
460: /*
461: * If the insertion point for adding the
462: * windows to the session falls inside the
463: * range of the list, then we insert these
464: * entries in order *AFTER* the selected
465: * session.
466: */
467: if (pos < i ) {
468: ARRAY_INSERT(&data->list,
469: pos + 1,
470: ARRAY_ITEM(&data->old_list,
471: i));
472: } else {
473: /* Ran out of room, add to the end. */
474: ARRAY_ADD(&data->list,
475: ARRAY_ITEM(&data->old_list,
476: i));
477: }
478: }
479: }
480: }
481: }
482:
1.1 nicm 483: void
1.16 nicm 484: window_choose_key(struct window_pane *wp, unused struct session *sess, int key)
1.1 nicm 485: {
486: struct window_choose_mode_data *data = wp->modedata;
487: struct screen *s = &data->screen;
488: struct screen_write_ctx ctx;
489: struct window_choose_mode_item *item;
1.22 nicm 490: size_t input_len;
1.27 nicm 491: u_int items, n;
1.11 nicm 492: int idx;
1.1 nicm 493:
494: items = ARRAY_LENGTH(&data->list);
495:
1.25 nicm 496: if (data->input_type == WINDOW_CHOOSE_GOTO_ITEM) {
497: switch (mode_key_lookup(&data->mdata, key)) {
498: case MODEKEYCHOICE_CANCEL:
499: data->input_type = WINDOW_CHOOSE_NORMAL;
500: window_choose_redraw_screen(wp);
1.22 nicm 501: break;
1.25 nicm 502: case MODEKEYCHOICE_CHOOSE:
1.22 nicm 503: n = strtonum(data->input_str, 0, INT_MAX, NULL);
1.25 nicm 504: if (n > items - 1) {
505: data->input_type = WINDOW_CHOOSE_NORMAL;
506: window_choose_redraw_screen(wp);
1.22 nicm 507: break;
1.25 nicm 508: }
1.22 nicm 509: item = &ARRAY_ITEM(&data->list, n);
510: window_choose_fire_callback(wp, item->wcd);
511: break;
1.25 nicm 512: case MODEKEYCHOICE_BACKSPACE:
513: input_len = strlen(data->input_str);
514: if (input_len > 0)
515: data->input_str[input_len - 1] = '\0';
516: window_choose_redraw_screen(wp);
517: break;
518: default:
519: if (key < '0' || key > '9')
520: break;
521: window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
522: "Goto Item", wp, key);
523: break;
1.22 nicm 524: }
1.25 nicm 525: return;
526: }
527:
528: switch (mode_key_lookup(&data->mdata, key)) {
529: case MODEKEYCHOICE_CANCEL:
530: window_choose_fire_callback(wp, NULL);
531: break;
532: case MODEKEYCHOICE_CHOOSE:
533: item = &ARRAY_ITEM(&data->list, data->selected);
534: window_choose_fire_callback(wp, item->wcd);
1.1 nicm 535: break;
1.27 nicm 536: case MODEKEYCHOICE_TREE_TOGGLE:
537: item = &ARRAY_ITEM(&data->list, data->selected);
538: if (item->state & TREE_EXPANDED)
539: window_choose_collapse(wp, item->wcd->tree_session);
540: else {
541: window_choose_expand(wp, item->wcd->tree_session,
542: data->selected);
543: }
544: window_choose_redraw_screen(wp);
545: break;
546: case MODEKEYCHOICE_TREE_COLLAPSE:
547: item = &ARRAY_ITEM(&data->list, data->selected);
548: if (item->state & TREE_EXPANDED) {
549: window_choose_collapse(wp, item->wcd->tree_session);
550: window_choose_redraw_screen(wp);
551: }
552: break;
553: case MODEKEYCHOICE_TREE_COLLAPSE_ALL:
554: window_choose_collapse_all(wp);
555: break;
556: case MODEKEYCHOICE_TREE_EXPAND:
557: item = &ARRAY_ITEM(&data->list, data->selected);
558: if (!(item->state & TREE_EXPANDED)) {
559: window_choose_expand(wp, item->wcd->tree_session,
560: data->selected);
561: window_choose_redraw_screen(wp);
562: }
563: break;
564: case MODEKEYCHOICE_TREE_EXPAND_ALL:
565: window_choose_expand_all(wp);
566: break;
1.5 nicm 567: case MODEKEYCHOICE_UP:
1.1 nicm 568: if (items == 0)
569: break;
570: if (data->selected == 0) {
571: data->selected = items - 1;
572: if (data->selected > screen_size_y(s) - 1)
573: data->top = items - screen_size_y(s);
574: window_choose_redraw_screen(wp);
575: break;
576: }
577: data->selected--;
578: if (data->selected < data->top)
579: window_choose_scroll_up(wp);
580: else {
581: screen_write_start(&ctx, wp, NULL);
582: window_choose_write_line(
583: wp, &ctx, data->selected - data->top);
584: window_choose_write_line(
585: wp, &ctx, data->selected + 1 - data->top);
586: screen_write_stop(&ctx);
587: }
588: break;
1.5 nicm 589: case MODEKEYCHOICE_DOWN:
1.1 nicm 590: if (items == 0)
591: break;
592: if (data->selected == items - 1) {
593: data->selected = 0;
594: data->top = 0;
595: window_choose_redraw_screen(wp);
596: break;
597: }
598: data->selected++;
1.5 nicm 599:
1.15 nicm 600: if (data->selected < data->top + screen_size_y(s)) {
1.1 nicm 601: screen_write_start(&ctx, wp, NULL);
602: window_choose_write_line(
603: wp, &ctx, data->selected - data->top);
604: window_choose_write_line(
605: wp, &ctx, data->selected - 1 - data->top);
606: screen_write_stop(&ctx);
1.15 nicm 607: } else
608: window_choose_scroll_down(wp);
609: break;
610: case MODEKEYCHOICE_SCROLLUP:
611: if (items == 0 || data->top == 0)
612: break;
613: if (data->selected == data->top + screen_size_y(s) - 1) {
614: data->selected--;
615: window_choose_scroll_up(wp);
616: screen_write_start(&ctx, wp, NULL);
617: window_choose_write_line(
618: wp, &ctx, screen_size_y(s) - 1);
619: screen_write_stop(&ctx);
620: } else
621: window_choose_scroll_up(wp);
622: break;
623: case MODEKEYCHOICE_SCROLLDOWN:
624: if (items == 0 ||
625: data->top + screen_size_y(&data->screen) >= items)
626: break;
627: if (data->selected == data->top) {
628: data->selected++;
629: window_choose_scroll_down(wp);
630: screen_write_start(&ctx, wp, NULL);
631: window_choose_write_line(wp, &ctx, 0);
632: screen_write_stop(&ctx);
633: } else
634: window_choose_scroll_down(wp);
1.1 nicm 635: break;
1.5 nicm 636: case MODEKEYCHOICE_PAGEUP:
1.1 nicm 637: if (data->selected < screen_size_y(s)) {
638: data->selected = 0;
639: data->top = 0;
640: } else {
641: data->selected -= screen_size_y(s);
642: if (data->top < screen_size_y(s))
643: data->top = 0;
644: else
645: data->top -= screen_size_y(s);
646: }
1.13 nicm 647: window_choose_redraw_screen(wp);
1.1 nicm 648: break;
1.5 nicm 649: case MODEKEYCHOICE_PAGEDOWN:
1.1 nicm 650: data->selected += screen_size_y(s);
651: if (data->selected > items - 1)
652: data->selected = items - 1;
653: data->top += screen_size_y(s);
654: if (screen_size_y(s) < items) {
655: if (data->top + screen_size_y(s) > items)
656: data->top = items - screen_size_y(s);
657: } else
658: data->top = 0;
659: if (data->selected < data->top)
660: data->top = data->selected;
661: window_choose_redraw_screen(wp);
662: break;
1.22 nicm 663: case MODEKEYCHOICE_BACKSPACE:
664: input_len = strlen(data->input_str);
665: if (input_len > 0)
666: data->input_str[input_len - 1] = '\0';
667: window_choose_redraw_screen(wp);
668: break;
669: case MODEKEYCHOICE_STARTNUMBERPREFIX:
1.25 nicm 670: key &= KEYC_MASK_KEY;
671: if (key < '0' || key > '9')
1.11 nicm 672: break;
1.25 nicm 673: window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
674: "Goto Item", wp, key);
1.22 nicm 675: break;
676: default:
1.25 nicm 677: idx = window_choose_index_key(data, key);
678: if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list))
679: break;
680: data->selected = idx;
681:
682: item = &ARRAY_ITEM(&data->list, data->selected);
683: window_choose_fire_callback(wp, item->wcd);
1.1 nicm 684: break;
685: }
686: }
687:
688: void
1.10 nicm 689: window_choose_mouse(
1.16 nicm 690: struct window_pane *wp, unused struct session *sess, struct mouse_event *m)
1.1 nicm 691: {
692: struct window_choose_mode_data *data = wp->modedata;
693: struct screen *s = &data->screen;
694: struct window_choose_mode_item *item;
695: u_int idx;
696:
1.29 nicm 697: if (~m->event & MOUSE_EVENT_CLICK)
1.1 nicm 698: return;
1.10 nicm 699: if (m->x >= screen_size_x(s))
1.1 nicm 700: return;
1.10 nicm 701: if (m->y >= screen_size_y(s))
1.1 nicm 702: return;
703:
1.10 nicm 704: idx = data->top + m->y;
1.1 nicm 705: if (idx >= ARRAY_LENGTH(&data->list))
706: return;
707: data->selected = idx;
708:
709: item = &ARRAY_ITEM(&data->list, data->selected);
1.19 nicm 710: window_choose_fire_callback(wp, item->wcd);
1.1 nicm 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: }