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