Annotation of src/usr.bin/tmux/window-choose.c, Revision 1.58
1.58 ! nicm 1: /* $OpenBSD: window-choose.c,v 1.57 2014/11/30 08:03: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.56 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:
1.55 nicm 320: data->input_str = xrealloc(data->input_str, input_len);
1.22 nicm 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;
1.58 ! nicm 333: u_int i;
1.27 nicm 334:
335: ARRAY_DECL(, struct window_choose_mode_item) list_copy;
336: ARRAY_INIT(&list_copy);
337:
1.58 ! nicm 338: chosen = &ARRAY_ITEM(&data->list, data->selected);
1.27 nicm 339: chosen->state &= ~TREE_EXPANDED;
340:
341: /*
342: * Trying to mangle the &data->list in-place has lots of problems, so
343: * assign the actual result we want to render and copy the new one over
344: * the top of it.
345: */
1.49 nicm 346: for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
1.27 nicm 347: item = &ARRAY_ITEM(&data->list, i);
348: wcd = item->wcd;
349:
350: if (s == wcd->tree_session) {
351: /* We only show the session when collapsed. */
352: if (wcd->type & TREE_SESSION) {
353: item->state &= ~TREE_EXPANDED;
1.58 ! nicm 354: ARRAY_ADD(&list_copy, *item);
1.27 nicm 355:
356: /*
357: * Update the selection to this session item so
358: * we don't end up highlighting a non-existent
359: * item.
360: */
361: data->selected = i;
362: }
363: } else
364: ARRAY_ADD(&list_copy, ARRAY_ITEM(&data->list, i));
365: }
366:
367: if (!ARRAY_EMPTY(&list_copy)) {
368: ARRAY_FREE(&data->list);
369: ARRAY_CONCAT(&data->list, &list_copy);
1.28 nicm 370: ARRAY_FREE(&list_copy);
1.27 nicm 371: }
372: }
373:
374: void
375: window_choose_collapse_all(struct window_pane *wp)
376: {
377: struct window_choose_mode_data *data = wp->modedata;
1.28 nicm 378: struct window_choose_mode_item *item;
1.48 nicm 379: struct screen *scr = &data->screen;
1.28 nicm 380: struct session *s, *chosen;
1.27 nicm 381: u_int i;
382:
1.33 nicm 383: chosen = ARRAY_ITEM(&data->list, data->selected).wcd->start_session;
1.27 nicm 384:
385: RB_FOREACH(s, sessions, &sessions)
386: window_choose_collapse(wp, s);
387:
388: /* Reset the selection back to the starting session. */
389: for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
390: item = &ARRAY_ITEM(&data->list, i);
391:
1.28 nicm 392: if (chosen != item->wcd->tree_session)
1.27 nicm 393: continue;
394:
395: if (item->wcd->type & TREE_SESSION)
396: data->selected = i;
397: }
1.48 nicm 398: window_choose_reset_top(wp, screen_size_y(scr));
1.27 nicm 399: }
400:
401: void
402: window_choose_expand_all(struct window_pane *wp)
403: {
404: struct window_choose_mode_data *data = wp->modedata;
405: struct window_choose_mode_item *item;
1.48 nicm 406: struct screen *scr = &data->screen;
1.27 nicm 407: struct session *s;
408: u_int i;
409:
410: RB_FOREACH(s, sessions, &sessions) {
411: for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
412: item = &ARRAY_ITEM(&data->list, i);
413:
414: if (s != item->wcd->tree_session)
415: continue;
416:
417: if (item->wcd->type & TREE_SESSION)
418: window_choose_expand(wp, s, i);
419: }
420: }
421:
1.48 nicm 422: window_choose_reset_top(wp, screen_size_y(scr));
1.27 nicm 423: }
424:
425: void
426: window_choose_expand(struct window_pane *wp, struct session *s, u_int pos)
427: {
428: struct window_choose_mode_data *data = wp->modedata;
429: struct window_choose_mode_item *item, *chosen;
430: struct window_choose_data *wcd;
431: u_int i, items;
432:
433: chosen = &ARRAY_ITEM(&data->list, pos);
434: items = ARRAY_LENGTH(&data->old_list) - 1;
435:
436: /* It's not possible to expand anything other than sessions. */
437: if (!(chosen->wcd->type & TREE_SESSION))
438: return;
439:
440: /* Don't re-expand a session which is already expanded. */
441: if (chosen->state & TREE_EXPANDED)
442: return;
443:
444: /* Mark the session entry as expanded. */
445: chosen->state |= TREE_EXPANDED;
446:
447: /*
448: * Go back through the original list of all sessions and windows, and
449: * pull out the windows where the session matches the selection chosen
450: * to expand.
451: */
452: for (i = items; i > 0; i--) {
453: item = &ARRAY_ITEM(&data->old_list, i);
454: item->state |= TREE_EXPANDED;
455: wcd = item->wcd;
456:
457: if (s == wcd->tree_session) {
458: /*
459: * Since the session is already displayed, we only care
460: * to add back in window for it.
461: */
462: if (wcd->type & TREE_WINDOW) {
463: /*
464: * If the insertion point for adding the
465: * windows to the session falls inside the
466: * range of the list, then we insert these
467: * entries in order *AFTER* the selected
468: * session.
469: */
470: if (pos < i ) {
471: ARRAY_INSERT(&data->list,
472: pos + 1,
473: ARRAY_ITEM(&data->old_list,
474: i));
475: } else {
476: /* Ran out of room, add to the end. */
477: ARRAY_ADD(&data->list,
478: ARRAY_ITEM(&data->old_list,
479: i));
480: }
481: }
482: }
483: }
484: }
485:
1.1 nicm 486: void
1.16 nicm 487: window_choose_key(struct window_pane *wp, unused struct session *sess, int key)
1.1 nicm 488: {
489: struct window_choose_mode_data *data = wp->modedata;
490: struct screen *s = &data->screen;
491: struct screen_write_ctx ctx;
492: struct window_choose_mode_item *item;
1.22 nicm 493: size_t input_len;
1.27 nicm 494: u_int items, n;
1.11 nicm 495: int idx;
1.1 nicm 496:
497: items = ARRAY_LENGTH(&data->list);
498:
1.25 nicm 499: if (data->input_type == WINDOW_CHOOSE_GOTO_ITEM) {
1.43 nicm 500: switch (mode_key_lookup(&data->mdata, key, NULL)) {
1.25 nicm 501: case MODEKEYCHOICE_CANCEL:
502: data->input_type = WINDOW_CHOOSE_NORMAL;
503: window_choose_redraw_screen(wp);
1.22 nicm 504: break;
1.25 nicm 505: case MODEKEYCHOICE_CHOOSE:
1.22 nicm 506: n = strtonum(data->input_str, 0, INT_MAX, NULL);
1.25 nicm 507: if (n > items - 1) {
508: data->input_type = WINDOW_CHOOSE_NORMAL;
509: window_choose_redraw_screen(wp);
1.22 nicm 510: break;
1.25 nicm 511: }
1.22 nicm 512: item = &ARRAY_ITEM(&data->list, n);
513: window_choose_fire_callback(wp, item->wcd);
514: break;
1.25 nicm 515: case MODEKEYCHOICE_BACKSPACE:
516: input_len = strlen(data->input_str);
517: if (input_len > 0)
518: data->input_str[input_len - 1] = '\0';
519: window_choose_redraw_screen(wp);
520: break;
521: default:
522: if (key < '0' || key > '9')
523: break;
524: window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
525: "Goto Item", wp, key);
526: break;
1.22 nicm 527: }
1.25 nicm 528: return;
529: }
530:
1.43 nicm 531: switch (mode_key_lookup(&data->mdata, key, NULL)) {
1.25 nicm 532: case MODEKEYCHOICE_CANCEL:
533: window_choose_fire_callback(wp, NULL);
534: break;
535: case MODEKEYCHOICE_CHOOSE:
536: item = &ARRAY_ITEM(&data->list, data->selected);
537: window_choose_fire_callback(wp, item->wcd);
1.1 nicm 538: break;
1.27 nicm 539: case MODEKEYCHOICE_TREE_TOGGLE:
540: item = &ARRAY_ITEM(&data->list, data->selected);
541: if (item->state & TREE_EXPANDED)
542: window_choose_collapse(wp, item->wcd->tree_session);
543: else {
544: window_choose_expand(wp, item->wcd->tree_session,
545: data->selected);
546: }
547: window_choose_redraw_screen(wp);
548: break;
549: case MODEKEYCHOICE_TREE_COLLAPSE:
550: item = &ARRAY_ITEM(&data->list, data->selected);
551: if (item->state & TREE_EXPANDED) {
552: window_choose_collapse(wp, item->wcd->tree_session);
553: window_choose_redraw_screen(wp);
554: }
555: break;
556: case MODEKEYCHOICE_TREE_COLLAPSE_ALL:
557: window_choose_collapse_all(wp);
558: break;
559: case MODEKEYCHOICE_TREE_EXPAND:
560: item = &ARRAY_ITEM(&data->list, data->selected);
561: if (!(item->state & TREE_EXPANDED)) {
562: window_choose_expand(wp, item->wcd->tree_session,
563: data->selected);
564: window_choose_redraw_screen(wp);
565: }
566: break;
567: case MODEKEYCHOICE_TREE_EXPAND_ALL:
568: window_choose_expand_all(wp);
569: break;
1.5 nicm 570: case MODEKEYCHOICE_UP:
1.1 nicm 571: if (items == 0)
572: break;
573: if (data->selected == 0) {
574: data->selected = items - 1;
575: if (data->selected > screen_size_y(s) - 1)
576: data->top = items - screen_size_y(s);
577: window_choose_redraw_screen(wp);
578: break;
579: }
580: data->selected--;
581: if (data->selected < data->top)
582: window_choose_scroll_up(wp);
583: else {
584: screen_write_start(&ctx, wp, NULL);
585: window_choose_write_line(
586: wp, &ctx, data->selected - data->top);
587: window_choose_write_line(
588: wp, &ctx, data->selected + 1 - data->top);
589: screen_write_stop(&ctx);
590: }
591: break;
1.5 nicm 592: case MODEKEYCHOICE_DOWN:
1.1 nicm 593: if (items == 0)
594: break;
595: if (data->selected == items - 1) {
596: data->selected = 0;
597: data->top = 0;
598: window_choose_redraw_screen(wp);
599: break;
600: }
601: data->selected++;
1.5 nicm 602:
1.15 nicm 603: if (data->selected < data->top + screen_size_y(s)) {
1.1 nicm 604: screen_write_start(&ctx, wp, NULL);
605: window_choose_write_line(
606: wp, &ctx, data->selected - data->top);
607: window_choose_write_line(
608: wp, &ctx, data->selected - 1 - data->top);
609: screen_write_stop(&ctx);
1.15 nicm 610: } else
611: window_choose_scroll_down(wp);
612: break;
613: case MODEKEYCHOICE_SCROLLUP:
614: if (items == 0 || data->top == 0)
615: break;
616: if (data->selected == data->top + screen_size_y(s) - 1) {
617: data->selected--;
618: window_choose_scroll_up(wp);
619: screen_write_start(&ctx, wp, NULL);
620: window_choose_write_line(
621: wp, &ctx, screen_size_y(s) - 1);
622: screen_write_stop(&ctx);
623: } else
624: window_choose_scroll_up(wp);
625: break;
626: case MODEKEYCHOICE_SCROLLDOWN:
627: if (items == 0 ||
628: data->top + screen_size_y(&data->screen) >= items)
629: break;
630: if (data->selected == data->top) {
631: data->selected++;
632: window_choose_scroll_down(wp);
633: screen_write_start(&ctx, wp, NULL);
634: window_choose_write_line(wp, &ctx, 0);
635: screen_write_stop(&ctx);
636: } else
637: window_choose_scroll_down(wp);
1.1 nicm 638: break;
1.5 nicm 639: case MODEKEYCHOICE_PAGEUP:
1.1 nicm 640: if (data->selected < screen_size_y(s)) {
641: data->selected = 0;
642: data->top = 0;
643: } else {
644: data->selected -= screen_size_y(s);
645: if (data->top < screen_size_y(s))
646: data->top = 0;
647: else
648: data->top -= screen_size_y(s);
649: }
1.13 nicm 650: window_choose_redraw_screen(wp);
1.1 nicm 651: break;
1.5 nicm 652: case MODEKEYCHOICE_PAGEDOWN:
1.1 nicm 653: data->selected += screen_size_y(s);
654: if (data->selected > items - 1)
655: data->selected = items - 1;
656: data->top += screen_size_y(s);
657: if (screen_size_y(s) < items) {
658: if (data->top + screen_size_y(s) > items)
659: data->top = items - screen_size_y(s);
660: } else
661: data->top = 0;
662: if (data->selected < data->top)
663: data->top = data->selected;
664: window_choose_redraw_screen(wp);
665: break;
1.22 nicm 666: case MODEKEYCHOICE_BACKSPACE:
667: input_len = strlen(data->input_str);
668: if (input_len > 0)
669: data->input_str[input_len - 1] = '\0';
670: window_choose_redraw_screen(wp);
671: break;
672: case MODEKEYCHOICE_STARTNUMBERPREFIX:
1.25 nicm 673: key &= KEYC_MASK_KEY;
674: if (key < '0' || key > '9')
1.11 nicm 675: break;
1.25 nicm 676: window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
677: "Goto Item", wp, key);
1.51 nicm 678: break;
679: case MODEKEYCHOICE_STARTOFLIST:
680: data->selected = 0;
681: data->top = 0;
682: window_choose_redraw_screen(wp);
683: break;
684: case MODEKEYCHOICE_TOPLINE:
685: data->selected = data->top;
686: window_choose_redraw_screen(wp);
687: break;
688: case MODEKEYCHOICE_BOTTOMLINE:
689: data->selected = data->top + screen_size_y(s) - 1;
690: if (data->selected > items - 1)
691: data->selected = items - 1;
692: window_choose_redraw_screen(wp);
693: break;
694: case MODEKEYCHOICE_ENDOFLIST:
695: data->selected = items - 1;
696: if (screen_size_y(s) < items)
697: data->top = items - screen_size_y(s);
698: else
699: data->top = 0;
700: window_choose_redraw_screen(wp);
1.22 nicm 701: break;
702: default:
1.25 nicm 703: idx = window_choose_index_key(data, key);
704: if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list))
705: break;
706: data->selected = idx;
707:
708: item = &ARRAY_ITEM(&data->list, data->selected);
709: window_choose_fire_callback(wp, item->wcd);
1.1 nicm 710: break;
711: }
712: }
713:
714: void
1.53 nicm 715: window_choose_mouse(struct window_pane *wp, struct session *sess,
716: struct mouse_event *m)
1.1 nicm 717: {
718: struct window_choose_mode_data *data = wp->modedata;
719: struct screen *s = &data->screen;
720: struct window_choose_mode_item *item;
1.54 nicm 721: u_int idx, i, n;
1.52 nicm 722:
723: if (m->event == MOUSE_EVENT_WHEEL) {
1.53 nicm 724: /*
1.54 nicm 725: * Multiple line scrolling by default is annoying, so scale
726: * m->scroll back down.
1.53 nicm 727: */
1.54 nicm 728: n = m->scroll;
729: if (n >= MOUSE_WHEEL_SCALE)
730: n /= MOUSE_WHEEL_SCALE;
731: for (i = 0; i < n; i++) {
732: if (m->wheel == MOUSE_WHEEL_UP)
733: window_choose_key(wp, sess, KEYC_UP);
734: else
735: window_choose_key(wp, sess, KEYC_DOWN);
736: }
1.52 nicm 737: return;
738: }
1.1 nicm 739:
1.29 nicm 740: if (~m->event & MOUSE_EVENT_CLICK)
1.1 nicm 741: return;
1.10 nicm 742: if (m->x >= screen_size_x(s))
1.1 nicm 743: return;
1.10 nicm 744: if (m->y >= screen_size_y(s))
1.1 nicm 745: return;
746:
1.10 nicm 747: idx = data->top + m->y;
1.1 nicm 748: if (idx >= ARRAY_LENGTH(&data->list))
749: return;
750: data->selected = idx;
751:
752: item = &ARRAY_ITEM(&data->list, data->selected);
1.19 nicm 753: window_choose_fire_callback(wp, item->wcd);
1.1 nicm 754: }
755:
756: void
757: window_choose_write_line(
758: struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
759: {
760: struct window_choose_mode_data *data = wp->modedata;
761: struct window_choose_mode_item *item;
1.9 nicm 762: struct options *oo = &wp->window->options;
1.1 nicm 763: struct screen *s = &data->screen;
764: struct grid_cell gc;
1.22 nicm 765: size_t last, xoff = 0;
1.25 nicm 766: char hdr[32], label[32];
767: int utf8flag, key;
1.1 nicm 768:
1.3 nicm 769: if (data->callbackfn == NULL)
1.1 nicm 770: fatalx("called before callback assigned");
771:
1.22 nicm 772: last = screen_size_y(s) - 1;
1.2 nicm 773: utf8flag = options_get_number(&wp->window->options, "utf8");
1.1 nicm 774: memcpy(&gc, &grid_default_cell, sizeof gc);
1.22 nicm 775: if (data->selected == data->top + py)
1.50 nicm 776: style_apply(&gc, oo, "mode-style");
1.1 nicm 777:
778: screen_write_cursormove(ctx, 0, py);
779: if (data->top + py < ARRAY_LENGTH(&data->list)) {
780: item = &ARRAY_ITEM(&data->list, data->top + py);
1.24 nicm 781: if (item->wcd->wl != NULL &&
782: item->wcd->wl->flags & WINLINK_ALERTFLAGS)
783: gc.attr |= GRID_ATTR_BRIGHT;
1.11 nicm 784:
1.25 nicm 785: key = window_choose_key_index(data, data->top + py);
786: if (key != -1)
1.56 nicm 787: xsnprintf(label, sizeof label, "(%c)", key);
1.25 nicm 788: else
1.56 nicm 789: xsnprintf(label, sizeof label, "(%d)", item->pos);
1.25 nicm 790: screen_write_nputs(ctx, screen_size_x(s) - 1, &gc, utf8flag,
1.27 nicm 791: "%*s %s %s", data->width + 2, label,
792: /*
793: * Add indication to tree if necessary about whether it's
794: * expanded or not.
795: */
796: (item->wcd->type & TREE_SESSION) ?
797: (item->state & TREE_EXPANDED ? "-" : "+") : "", item->name);
1.1 nicm 798: }
1.40 nicm 799: while (s->cx < screen_size_x(s) - 1)
1.1 nicm 800: screen_write_putc(ctx, &gc, ' ');
1.11 nicm 801:
1.22 nicm 802: if (data->input_type != WINDOW_CHOOSE_NORMAL) {
1.50 nicm 803: style_apply(&gc, oo, "mode-style");
1.11 nicm 804:
1.22 nicm 805: xoff = xsnprintf(hdr, sizeof hdr,
806: "%s: %s", data->input_prompt, data->input_str);
807: screen_write_cursormove(ctx, 0, last);
808: screen_write_puts(ctx, &gc, "%s", hdr);
809: screen_write_cursormove(ctx, xoff, py);
810: memcpy(&gc, &grid_default_cell, sizeof gc);
1.11 nicm 811: }
1.22 nicm 812:
1.11 nicm 813: }
814:
815: int
1.25 nicm 816: window_choose_key_index(struct window_choose_mode_data *data, u_int idx)
817: {
818: static const char keys[] = "0123456789"
819: "abcdefghijklmnopqrstuvwxyz"
820: "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
821: const char *ptr;
822: int mkey;
823:
824: for (ptr = keys; *ptr != '\0'; ptr++) {
1.43 nicm 825: mkey = mode_key_lookup(&data->mdata, *ptr, NULL);
1.25 nicm 826: if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER)
827: continue;
828: if (idx-- == 0)
829: return (*ptr);
830: }
831: return (-1);
832: }
833:
834: int
835: window_choose_index_key(struct window_choose_mode_data *data, int key)
1.11 nicm 836: {
1.25 nicm 837: static const char keys[] = "0123456789"
838: "abcdefghijklmnopqrstuvwxyz"
839: "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1.11 nicm 840: const char *ptr;
1.25 nicm 841: int mkey;
1.11 nicm 842: u_int idx = 0;
843:
844: for (ptr = keys; *ptr != '\0'; ptr++) {
1.43 nicm 845: mkey = mode_key_lookup(&data->mdata, *ptr, NULL);
1.25 nicm 846: if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER)
847: continue;
1.11 nicm 848: if (key == *ptr)
849: return (idx);
850: idx++;
851: }
852: return (-1);
1.1 nicm 853: }
854:
855: void
856: window_choose_redraw_screen(struct window_pane *wp)
857: {
858: struct window_choose_mode_data *data = wp->modedata;
859: struct screen *s = &data->screen;
860: struct screen_write_ctx ctx;
861: u_int i;
862:
863: screen_write_start(&ctx, wp, NULL);
864: for (i = 0; i < screen_size_y(s); i++)
865: window_choose_write_line(wp, &ctx, i);
866: screen_write_stop(&ctx);
867: }
868:
869: void
870: window_choose_scroll_up(struct window_pane *wp)
871: {
872: struct window_choose_mode_data *data = wp->modedata;
873: struct screen_write_ctx ctx;
874:
875: if (data->top == 0)
876: return;
877: data->top--;
878:
879: screen_write_start(&ctx, wp, NULL);
880: screen_write_cursormove(&ctx, 0, 0);
881: screen_write_insertline(&ctx, 1);
882: window_choose_write_line(wp, &ctx, 0);
883: if (screen_size_y(&data->screen) > 1)
884: window_choose_write_line(wp, &ctx, 1);
885: screen_write_stop(&ctx);
886: }
887:
888: void
889: window_choose_scroll_down(struct window_pane *wp)
890: {
891: struct window_choose_mode_data *data = wp->modedata;
892: struct screen *s = &data->screen;
893: struct screen_write_ctx ctx;
894:
895: if (data->top >= ARRAY_LENGTH(&data->list))
896: return;
897: data->top++;
898:
899: screen_write_start(&ctx, wp, NULL);
900: screen_write_cursormove(&ctx, 0, 0);
901: screen_write_deleteline(&ctx, 1);
902: window_choose_write_line(wp, &ctx, screen_size_y(s) - 1);
903: if (screen_size_y(&data->screen) > 1)
904: window_choose_write_line(wp, &ctx, screen_size_y(s) - 2);
905: screen_write_stop(&ctx);
1.19 nicm 906: }
907:
1.20 nicm 908: struct window_choose_data *
1.35 nicm 909: window_choose_add_session(struct window_pane *wp, struct client *c,
1.36 nicm 910: struct session *s, const char *template, const char *action, u_int idx)
1.20 nicm 911: {
912: struct window_choose_data *wcd;
913:
1.33 nicm 914: wcd = window_choose_data_create(TREE_SESSION, c, c->session);
1.46 nicm 915: wcd->idx = s->id;
1.33 nicm 916:
1.27 nicm 917: wcd->tree_session = s;
1.33 nicm 918: wcd->tree_session->references++;
919:
1.20 nicm 920: wcd->ft_template = xstrdup(template);
921: format_add(wcd->ft, "line", "%u", idx);
922: format_session(wcd->ft, s);
923:
1.33 nicm 924: wcd->command = cmd_template_replace(action, s->name, 1);
1.20 nicm 925:
926: window_choose_add(wp, wcd);
927:
928: return (wcd);
929: }
930:
931: struct window_choose_data *
1.35 nicm 932: window_choose_add_window(struct window_pane *wp, struct client *c,
1.22 nicm 933: struct session *s, struct winlink *wl, const char *template,
1.36 nicm 934: const char *action, u_int idx)
1.20 nicm 935: {
936: struct window_choose_data *wcd;
1.33 nicm 937: char *expanded;
1.20 nicm 938:
1.33 nicm 939: wcd = window_choose_data_create(TREE_WINDOW, c, c->session);
940: wcd->idx = wl->idx;
1.20 nicm 941:
1.33 nicm 942: wcd->wl = wl;
1.20 nicm 943:
1.27 nicm 944: wcd->tree_session = s;
1.33 nicm 945: wcd->tree_session->references++;
946:
1.20 nicm 947: wcd->ft_template = xstrdup(template);
948: format_add(wcd->ft, "line", "%u", idx);
949: format_session(wcd->ft, s);
950: format_winlink(wcd->ft, s, wl);
951: format_window_pane(wcd->ft, wl->window->active);
952:
1.33 nicm 953: xasprintf(&expanded, "%s:%d", s->name, wl->idx);
954: wcd->command = cmd_template_replace(action, expanded, 1);
955: free(expanded);
1.20 nicm 956:
957: window_choose_add(wp, wcd);
958:
959: return (wcd);
1.1 nicm 960: }