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