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