Annotation of embedaddon/tmux/window-choose.c, revision 1.1.1.1
1.1 misho 1: /* $OpenBSD$ */
2:
3: /*
4: * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
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:
21: #include <ctype.h>
22: #include <stdlib.h>
23: #include <string.h>
24:
25: #include "tmux.h"
26:
27: static struct screen *window_choose_init(struct window_pane *);
28: static void window_choose_free(struct window_pane *);
29: static void window_choose_resize(struct window_pane *, u_int, u_int);
30: static void window_choose_key(struct window_pane *, struct client *,
31: struct session *, key_code, struct mouse_event *);
32:
33: static void window_choose_default_callback(struct window_choose_data *);
34: static struct window_choose_mode_item *window_choose_get_item(
35: struct window_pane *, key_code, struct mouse_event *);
36:
37: static void window_choose_fire_callback(struct window_pane *,
38: struct window_choose_data *);
39: static void window_choose_redraw_screen(struct window_pane *);
40: static void window_choose_write_line(struct window_pane *,
41: struct screen_write_ctx *, u_int);
42:
43: static void window_choose_scroll_up(struct window_pane *);
44: static void window_choose_scroll_down(struct window_pane *);
45:
46: static void window_choose_collapse(struct window_pane *, struct session *,
47: u_int);
48: static void window_choose_expand(struct window_pane *, struct session *,
49: u_int);
50: static void window_choose_collapse_all(struct window_pane *);
51:
52: static void window_choose_data_free(struct window_choose_data *);
53:
54: enum window_choose_input_type {
55: WINDOW_CHOOSE_NORMAL = -1,
56: WINDOW_CHOOSE_GOTO_ITEM,
57: };
58:
59: const struct window_mode window_choose_mode = {
60: .init = window_choose_init,
61: .free = window_choose_free,
62: .resize = window_choose_resize,
63: .key = window_choose_key,
64: };
65:
66: struct window_choose_mode_item {
67: struct window_choose_data *wcd;
68: char *name;
69: int pos;
70: int state;
71: #define TREE_EXPANDED 0x1
72: };
73:
74: struct window_choose_mode_data {
75: struct screen screen;
76:
77: struct window_choose_mode_item *list;
78: u_int list_size;
79: struct window_choose_mode_item *old_list;
80: u_int old_list_size;
81:
82: int width;
83: u_int top;
84: u_int selected;
85: enum window_choose_input_type input_type;
86: const char *input_prompt;
87: char *input_str;
88:
89: void (*callbackfn)(struct window_choose_data *);
90: };
91:
92: static const char window_choose_keys_emacs[] = "0123456789"
93: "abcdefghijklmnoprstuvwxyz"
94: "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
95: static const char window_choose_keys_vi[] = "0123456789"
96: "abcdefimnoprstuvwxyz"
97: "ABCDEFIJKMNOPQRSTUVWXYZ";
98:
99: static void window_choose_free1(struct window_choose_mode_data *);
100: static int window_choose_key_index(struct window_pane *, u_int);
101: static int window_choose_index_key(struct window_pane *, key_code);
102: static void window_choose_prompt_input(enum window_choose_input_type,
103: const char *, struct window_pane *, key_code);
104: static void window_choose_reset_top(struct window_pane *, u_int);
105:
106: void
107: window_choose_add(struct window_pane *wp, struct window_choose_data *wcd)
108: {
109: struct window_choose_mode_data *data = wp->modedata;
110: struct window_choose_mode_item *item;
111: char tmp[11];
112:
113: data->list = xreallocarray(data->list, data->list_size + 1,
114: sizeof *data->list);
115: item = &data->list[data->list_size++];
116:
117: item->name = format_expand(wcd->ft, wcd->ft_template);
118: item->wcd = wcd;
119: item->pos = data->list_size - 1;
120: item->state = 0;
121:
122: data->width = xsnprintf(tmp, sizeof tmp, "%d", item->pos);
123: }
124:
125: void
126: window_choose_set_current(struct window_pane *wp, u_int cur)
127: {
128: struct window_choose_mode_data *data = wp->modedata;
129: struct screen *s = &data->screen;
130:
131: data->selected = cur;
132: window_choose_reset_top(wp, screen_size_y(s));
133: }
134:
135: static void
136: window_choose_reset_top(struct window_pane *wp, u_int sy)
137: {
138: struct window_choose_mode_data *data = wp->modedata;
139:
140: data->top = 0;
141: if (data->selected > sy - 1)
142: data->top = data->selected - (sy - 1);
143:
144: window_choose_redraw_screen(wp);
145: }
146:
147: void
148: window_choose_ready(struct window_pane *wp, u_int cur,
149: void (*callbackfn)(struct window_choose_data *))
150: {
151: struct window_choose_mode_data *data = wp->modedata;
152: u_int size;
153:
154: data->callbackfn = callbackfn;
155: if (data->callbackfn == NULL)
156: data->callbackfn = window_choose_default_callback;
157:
158: size = data->old_list_size;
159: data->old_list_size += data->list_size;
160: data->old_list = xreallocarray(data->old_list, data->old_list_size,
161: sizeof *data->old_list);
162: memcpy(data->old_list + size, data->list, data->list_size *
163: sizeof *data->list);
164:
165: window_choose_set_current(wp, cur);
166: window_choose_collapse_all(wp);
167: }
168:
169: static struct screen *
170: window_choose_init(struct window_pane *wp)
171: {
172: struct window_choose_mode_data *data;
173: struct screen *s;
174:
175: wp->modedata = data = xcalloc(1, sizeof *data);
176:
177: data->callbackfn = NULL;
178: data->input_type = WINDOW_CHOOSE_NORMAL;
179: data->input_str = xstrdup("");
180: data->input_prompt = NULL;
181:
182: data->list = NULL;
183: data->list_size = 0;
184:
185: data->old_list = NULL;
186: data->old_list_size = 0;
187:
188: data->top = 0;
189:
190: s = &data->screen;
191: screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
192: s->mode &= ~MODE_CURSOR;
193:
194: return (s);
195: }
196:
197: struct window_choose_data *
198: window_choose_data_create(int type, struct client *c, struct session *s)
199: {
200: struct window_choose_data *wcd;
201:
202: wcd = xmalloc(sizeof *wcd);
203: wcd->type = type;
204:
205: wcd->ft = format_create(NULL, FORMAT_NONE, 0);
206: wcd->ft_template = NULL;
207:
208: wcd->command = NULL;
209:
210: wcd->wl = NULL;
211: wcd->pane_id = -1;
212: wcd->idx = -1;
213:
214: wcd->tree_session = NULL;
215:
216: wcd->start_client = c;
217: wcd->start_client->references++;
218: wcd->start_session = s;
219: wcd->start_session->references++;
220:
221: return (wcd);
222: }
223:
224: static void
225: window_choose_data_free(struct window_choose_data *wcd)
226: {
227: server_client_unref(wcd->start_client);
228: session_unref(wcd->start_session);
229:
230: if (wcd->tree_session != NULL)
231: session_unref(wcd->tree_session);
232:
233: free(wcd->ft_template);
234: format_free(wcd->ft);
235:
236: free(wcd->command);
237: free(wcd);
238: }
239:
240: void
241: window_choose_data_run(struct window_choose_data *cdata)
242: {
243: struct cmd_list *cmdlist;
244: char *cause;
245: struct cmdq_item *item;
246:
247: /*
248: * The command template will have already been replaced. But if it's
249: * NULL, bail here.
250: */
251: if (cdata->command == NULL)
252: return;
253:
254: cmdlist = cmd_string_parse(cdata->command, NULL, 0, &cause);
255: if (cmdlist == NULL) {
256: if (cause != NULL) {
257: *cause = toupper((u_char) *cause);
258: status_message_set(cdata->start_client, "%s", cause);
259: free(cause);
260: }
261: return;
262: }
263:
264: item = cmdq_get_command(cmdlist, NULL, NULL, 0);
265: cmdq_append(cdata->start_client, item);
266: cmd_list_free(cmdlist);
267: }
268:
269: static void
270: window_choose_default_callback(struct window_choose_data *wcd)
271: {
272: if (wcd == NULL)
273: return;
274: if (wcd->start_client->flags & CLIENT_DEAD)
275: return;
276:
277: window_choose_data_run(wcd);
278: }
279:
280: static void
281: window_choose_free(struct window_pane *wp)
282: {
283: if (wp->modedata != NULL)
284: window_choose_free1(wp->modedata);
285: }
286:
287: static void
288: window_choose_free1(struct window_choose_mode_data *data)
289: {
290: struct window_choose_mode_item *item;
291: u_int i;
292:
293: if (data == NULL)
294: return;
295:
296: for (i = 0; i < data->old_list_size; i++) {
297: item = &data->old_list[i];
298: window_choose_data_free(item->wcd);
299: free(item->name);
300: }
301: free(data->list);
302: free(data->old_list);
303:
304: free(data->input_str);
305:
306: screen_free(&data->screen);
307: free(data);
308: }
309:
310: static void
311: window_choose_resize(struct window_pane *wp, u_int sx, u_int sy)
312: {
313: struct window_choose_mode_data *data = wp->modedata;
314: struct screen *s = &data->screen;
315:
316: window_choose_reset_top(wp, sy);
317: screen_resize(s, sx, sy, 0);
318: window_choose_redraw_screen(wp);
319: }
320:
321: static void
322: window_choose_fire_callback(struct window_pane *wp,
323: struct window_choose_data *wcd)
324: {
325: struct window_choose_mode_data *data = wp->modedata;
326:
327: wp->modedata = NULL;
328: window_pane_reset_mode(wp);
329:
330: data->callbackfn(wcd);
331:
332: window_choose_free1(data);
333: }
334:
335: static void
336: window_choose_prompt_input(enum window_choose_input_type input_type,
337: const char *prompt, struct window_pane *wp, key_code key)
338: {
339: struct window_choose_mode_data *data = wp->modedata;
340: size_t input_len;
341:
342: data->input_type = input_type;
343: data->input_prompt = prompt;
344: input_len = strlen(data->input_str) + 2;
345:
346: data->input_str = xrealloc(data->input_str, input_len);
347: data->input_str[input_len - 2] = key;
348: data->input_str[input_len - 1] = '\0';
349:
350: window_choose_redraw_screen(wp);
351: }
352:
353: static void
354: window_choose_collapse(struct window_pane *wp, struct session *s, u_int pos)
355: {
356: struct window_choose_mode_data *data = wp->modedata;
357: struct window_choose_mode_item *item, *chosen, *copy = NULL;
358: struct window_choose_data *wcd;
359: u_int i, copy_size = 0;
360:
361: chosen = &data->list[pos];
362: chosen->state &= ~TREE_EXPANDED;
363:
364: /*
365: * Trying to mangle the &data->list in-place has lots of problems, so
366: * assign the actual result we want to render and copy the new one over
367: * the top of it.
368: */
369: for (i = 0; i < data->list_size; i++) {
370: item = &data->list[i];
371: wcd = item->wcd;
372:
373: if (s == wcd->tree_session) {
374: /* We only show the session when collapsed. */
375: if (wcd->type & TREE_SESSION) {
376: item->state &= ~TREE_EXPANDED;
377:
378: copy = xreallocarray(copy, copy_size + 1,
379: sizeof *copy);
380: memcpy(©[copy_size], item, sizeof *copy);
381: copy_size++;
382:
383: /*
384: * Update the selection to this session item so
385: * we don't end up highlighting a non-existent
386: * item.
387: */
388: data->selected = i;
389: }
390: } else {
391: copy = xreallocarray(copy, copy_size + 1, sizeof *copy);
392: memcpy(©[copy_size], item, sizeof *copy);
393: copy_size++;
394: }
395: }
396:
397: if (copy_size != 0) {
398: free(data->list);
399: data->list = copy;
400: data->list_size = copy_size;
401: }
402: }
403:
404: static void
405: window_choose_collapse_all(struct window_pane *wp)
406: {
407: struct window_choose_mode_data *data = wp->modedata;
408: struct window_choose_mode_item *item;
409: struct screen *scr = &data->screen;
410: struct session *s, *chosen;
411: u_int i;
412:
413: chosen = data->list[data->selected].wcd->start_session;
414:
415: RB_FOREACH(s, sessions, &sessions)
416: window_choose_collapse(wp, s, data->selected);
417:
418: /* Reset the selection back to the starting session. */
419: for (i = 0; i < data->list_size; i++) {
420: item = &data->list[i];
421:
422: if (chosen != item->wcd->tree_session)
423: continue;
424:
425: if (item->wcd->type & TREE_SESSION)
426: data->selected = i;
427: }
428: window_choose_reset_top(wp, screen_size_y(scr));
429: }
430:
431: void
432: window_choose_expand_all(struct window_pane *wp)
433: {
434: struct window_choose_mode_data *data = wp->modedata;
435: struct window_choose_mode_item *item;
436: struct screen *scr = &data->screen;
437: struct session *s;
438: u_int i;
439:
440: RB_FOREACH(s, sessions, &sessions) {
441: for (i = 0; i < data->list_size; i++) {
442: item = &data->list[i];
443:
444: if (s != item->wcd->tree_session)
445: continue;
446:
447: if (item->wcd->type & TREE_SESSION)
448: window_choose_expand(wp, s, i);
449: }
450: }
451:
452: window_choose_reset_top(wp, screen_size_y(scr));
453: }
454:
455: static void
456: window_choose_expand(struct window_pane *wp, struct session *s, u_int pos)
457: {
458: struct window_choose_mode_data *data = wp->modedata;
459: struct window_choose_mode_item *item, *chosen;
460: struct window_choose_data *wcd;
461: u_int i, items;
462:
463: chosen = &data->list[pos];
464: items = data->old_list_size - 1;
465:
466: /* It's not possible to expand anything other than sessions. */
467: if (!(chosen->wcd->type & TREE_SESSION))
468: return;
469:
470: /* Don't re-expand a session which is already expanded. */
471: if (chosen->state & TREE_EXPANDED)
472: return;
473:
474: /* Mark the session entry as expanded. */
475: chosen->state |= TREE_EXPANDED;
476:
477: /*
478: * Go back through the original list of all sessions and windows, and
479: * pull out the windows where the session matches the selection chosen
480: * to expand.
481: */
482: for (i = items; i > 0; i--) {
483: item = &data->old_list[i];
484: item->state |= TREE_EXPANDED;
485: wcd = item->wcd;
486:
487: if (s == wcd->tree_session) {
488: /*
489: * Since the session is already displayed, we only care
490: * to add back in window for it.
491: */
492: if (wcd->type & TREE_WINDOW) {
493: /*
494: * If the insertion point for adding the
495: * windows to the session falls inside the
496: * range of the list, then we insert these
497: * entries in order *AFTER* the selected
498: * session.
499: */
500: if (pos < i) {
501: data->list = xreallocarray(data->list,
502: data->list_size + 1,
503: sizeof *data->list);
504: memmove(&data->list[pos + 2],
505: &data->list[pos + 1],
506: (data->list_size - (pos + 1)) *
507: sizeof *data->list);
508: memcpy(&data->list[pos + 1],
509: &data->old_list[i],
510: sizeof *data->list);
511: data->list_size++;
512: } else {
513: /* Ran out of room, add to the end. */
514: data->list = xreallocarray(data->list,
515: data->list_size + 1,
516: sizeof *data->list);
517: memcpy(&data->list[data->list_size],
518: &data->old_list[i],
519: sizeof *data->list);
520: data->list_size++;
521: }
522: }
523: }
524: }
525: }
526:
527: static struct window_choose_mode_item *
528: window_choose_get_item(struct window_pane *wp, key_code key,
529: struct mouse_event *m)
530: {
531: struct window_choose_mode_data *data = wp->modedata;
532: u_int x, y, idx;
533:
534: if (!KEYC_IS_MOUSE(key))
535: return (&data->list[data->selected]);
536:
537: if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
538: return (NULL);
539:
540: idx = data->top + y;
541: if (idx >= data->list_size)
542: return (NULL);
543: return (&data->list[idx]);
544: }
545:
546: static key_code
547: window_choose_translate_key(key_code key)
548: {
549: switch (key) {
550: case '0'|KEYC_ESCAPE:
551: case '1'|KEYC_ESCAPE:
552: case '2'|KEYC_ESCAPE:
553: case '3'|KEYC_ESCAPE:
554: case '4'|KEYC_ESCAPE:
555: case '5'|KEYC_ESCAPE:
556: case '6'|KEYC_ESCAPE:
557: case '7'|KEYC_ESCAPE:
558: case '8'|KEYC_ESCAPE:
559: case '9'|KEYC_ESCAPE:
560: case '\003': /* C-c */
561: case 'q':
562: case '\n':
563: case '\r':
564: case KEYC_BSPACE:
565: case ' ':
566: case KEYC_LEFT|KEYC_CTRL:
567: case KEYC_RIGHT|KEYC_CTRL:
568: case KEYC_MOUSEDOWN1_PANE:
569: case KEYC_MOUSEDOWN3_PANE:
570: case KEYC_WHEELUP_PANE:
571: case KEYC_WHEELDOWN_PANE:
572: return (key);
573: case '\031': /* C-y */
574: case KEYC_UP|KEYC_CTRL:
575: return (KEYC_UP|KEYC_CTRL);
576: case '\002': /* C-b */
577: case KEYC_PPAGE:
578: return (KEYC_PPAGE);
579: case '\005': /* C-e */
580: case KEYC_DOWN|KEYC_CTRL:
581: return (KEYC_DOWN|KEYC_CTRL);
582: case '\006': /* C-f */
583: case KEYC_NPAGE:
584: return (KEYC_NPAGE);
585: case 'h':
586: case KEYC_LEFT:
587: return (KEYC_LEFT);
588: case 'j':
589: case KEYC_DOWN:
590: return (KEYC_DOWN);
591: case 'k':
592: case KEYC_UP:
593: return (KEYC_UP);
594: case 'l':
595: case KEYC_RIGHT:
596: return (KEYC_RIGHT);
597: case 'g':
598: case KEYC_HOME:
599: return (KEYC_HOME);
600: case 'G':
601: case KEYC_END:
602: return (KEYC_END);
603: case 'H':
604: return ('R'|KEYC_ESCAPE);
605: case 'L':
606: return ('r'|KEYC_ESCAPE);
607: }
608: if ((key >= '0' && key <= '9') ||
609: (key >= 'a' && key <= 'z') ||
610: (key >= 'A' && key <= 'Z'))
611: return (key);
612: return (KEYC_NONE);
613: }
614:
615: static void
616: window_choose_key(struct window_pane *wp, __unused struct client *c,
617: __unused struct session *sp, key_code key, struct mouse_event *m)
618: {
619: struct window_choose_mode_data *data = wp->modedata;
620: struct screen *s = &data->screen;
621: struct screen_write_ctx ctx;
622: struct window_choose_mode_item *item;
623: size_t input_len;
624: u_int items, n;
625: int idx, keys;
626:
627: keys = options_get_number(wp->window->options, "mode-keys");
628: if (keys == MODEKEY_VI) {
629: key = window_choose_translate_key(key);
630: if (key == KEYC_NONE)
631: return;
632: }
633: items = data->list_size;
634:
635: if (data->input_type == WINDOW_CHOOSE_GOTO_ITEM) {
636: switch (key) {
637: case '\003': /* C-c */
638: case '\033': /* Escape */
639: case 'q':
640: data->input_type = WINDOW_CHOOSE_NORMAL;
641: window_choose_redraw_screen(wp);
642: break;
643: case '\n':
644: case '\r':
645: n = strtonum(data->input_str, 0, INT_MAX, NULL);
646: if (n > items - 1) {
647: data->input_type = WINDOW_CHOOSE_NORMAL;
648: window_choose_redraw_screen(wp);
649: break;
650: }
651: window_choose_fire_callback(wp, data->list[n].wcd);
652: break;
653: case KEYC_BSPACE:
654: input_len = strlen(data->input_str);
655: if (input_len > 0)
656: data->input_str[input_len - 1] = '\0';
657: window_choose_redraw_screen(wp);
658: break;
659: default:
660: if (key < '0' || key > '9')
661: break;
662: window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
663: "Goto Item", wp, key);
664: break;
665: }
666: return;
667: }
668:
669: switch (key) {
670: case '\003': /* C-c */
671: case '\033': /* Escape */
672: case 'q':
673: window_choose_fire_callback(wp, NULL);
674: break;
675: case '\n':
676: case '\r':
677: case KEYC_MOUSEDOWN1_PANE:
678: if ((item = window_choose_get_item(wp, key, m)) == NULL)
679: break;
680: window_choose_fire_callback(wp, item->wcd);
681: break;
682: case ' ':
683: case KEYC_MOUSEDOWN3_PANE:
684: if ((item = window_choose_get_item(wp, key, m)) == NULL)
685: break;
686: if (item->state & TREE_EXPANDED) {
687: window_choose_collapse(wp, item->wcd->tree_session,
688: data->selected);
689: } else {
690: window_choose_expand(wp, item->wcd->tree_session,
691: data->selected);
692: }
693: window_choose_redraw_screen(wp);
694: break;
695: case KEYC_LEFT:
696: if ((item = window_choose_get_item(wp, key, m)) == NULL)
697: break;
698: if (item->state & TREE_EXPANDED) {
699: window_choose_collapse(wp, item->wcd->tree_session,
700: data->selected);
701: window_choose_redraw_screen(wp);
702: }
703: break;
704: case KEYC_LEFT|KEYC_CTRL:
705: window_choose_collapse_all(wp);
706: break;
707: case KEYC_RIGHT:
708: if ((item = window_choose_get_item(wp, key, m)) == NULL)
709: break;
710: if (!(item->state & TREE_EXPANDED)) {
711: window_choose_expand(wp, item->wcd->tree_session,
712: data->selected);
713: window_choose_redraw_screen(wp);
714: }
715: break;
716: case KEYC_RIGHT|KEYC_CTRL:
717: window_choose_expand_all(wp);
718: break;
719: case '\020': /* C-p */
720: case KEYC_UP:
721: case KEYC_WHEELUP_PANE:
722: if (items == 0)
723: break;
724: if (data->selected == 0) {
725: data->selected = items - 1;
726: if (data->selected > screen_size_y(s) - 1)
727: data->top = items - screen_size_y(s);
728: window_choose_redraw_screen(wp);
729: break;
730: }
731: data->selected--;
732: if (data->selected < data->top)
733: window_choose_scroll_up(wp);
734: else {
735: screen_write_start(&ctx, wp, NULL);
736: window_choose_write_line(wp, &ctx,
737: data->selected - data->top);
738: window_choose_write_line(wp, &ctx,
739: data->selected + 1 - data->top);
740: screen_write_stop(&ctx);
741: }
742: break;
743: case '\016': /* C-n */
744: case KEYC_DOWN:
745: case KEYC_WHEELDOWN_PANE:
746: if (items == 0)
747: break;
748: if (data->selected == items - 1) {
749: data->selected = 0;
750: data->top = 0;
751: window_choose_redraw_screen(wp);
752: break;
753: }
754: data->selected++;
755:
756: if (data->selected < data->top + screen_size_y(s)) {
757: screen_write_start(&ctx, wp, NULL);
758: window_choose_write_line(wp, &ctx,
759: data->selected - data->top);
760: window_choose_write_line(wp, &ctx,
761: data->selected - 1 - data->top);
762: screen_write_stop(&ctx);
763: } else
764: window_choose_scroll_down(wp);
765: break;
766: case KEYC_UP|KEYC_CTRL:
767: if (items == 0 || data->top == 0)
768: break;
769: if (data->selected == data->top + screen_size_y(s) - 1) {
770: data->selected--;
771: window_choose_scroll_up(wp);
772: screen_write_start(&ctx, wp, NULL);
773: window_choose_write_line(wp, &ctx,
774: screen_size_y(s) - 1);
775: screen_write_stop(&ctx);
776: } else
777: window_choose_scroll_up(wp);
778: break;
779: case KEYC_DOWN|KEYC_CTRL:
780: if (items == 0 ||
781: data->top + screen_size_y(&data->screen) >= items)
782: break;
783: if (data->selected == data->top) {
784: data->selected++;
785: window_choose_scroll_down(wp);
786: screen_write_start(&ctx, wp, NULL);
787: window_choose_write_line(wp, &ctx, 0);
788: screen_write_stop(&ctx);
789: } else
790: window_choose_scroll_down(wp);
791: break;
792: case KEYC_PPAGE:
793: if (data->selected < screen_size_y(s)) {
794: data->selected = 0;
795: data->top = 0;
796: } else {
797: data->selected -= screen_size_y(s);
798: if (data->top < screen_size_y(s))
799: data->top = 0;
800: else
801: data->top -= screen_size_y(s);
802: }
803: window_choose_redraw_screen(wp);
804: break;
805: case KEYC_NPAGE:
806: data->selected += screen_size_y(s);
807: if (data->selected > items - 1)
808: data->selected = items - 1;
809: data->top += screen_size_y(s);
810: if (screen_size_y(s) < items) {
811: if (data->top + screen_size_y(s) > items)
812: data->top = items - screen_size_y(s);
813: } else
814: data->top = 0;
815: if (data->selected < data->top)
816: data->top = data->selected;
817: window_choose_redraw_screen(wp);
818: break;
819: case KEYC_BSPACE:
820: input_len = strlen(data->input_str);
821: if (input_len > 0)
822: data->input_str[input_len - 1] = '\0';
823: window_choose_redraw_screen(wp);
824: break;
825: case '0'|KEYC_ESCAPE:
826: case '1'|KEYC_ESCAPE:
827: case '2'|KEYC_ESCAPE:
828: case '3'|KEYC_ESCAPE:
829: case '4'|KEYC_ESCAPE:
830: case '5'|KEYC_ESCAPE:
831: case '6'|KEYC_ESCAPE:
832: case '7'|KEYC_ESCAPE:
833: case '8'|KEYC_ESCAPE:
834: case '9'|KEYC_ESCAPE:
835: key &= KEYC_MASK_KEY;
836: if (key < '0' || key > '9')
837: break;
838: window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
839: "Goto Item", wp, key);
840: break;
841: case KEYC_HOME:
842: case '<'|KEYC_ESCAPE:
843: data->selected = 0;
844: data->top = 0;
845: window_choose_redraw_screen(wp);
846: break;
847: case 'R'|KEYC_ESCAPE:
848: data->selected = data->top;
849: window_choose_redraw_screen(wp);
850: break;
851: case 'r'|KEYC_ESCAPE:
852: data->selected = data->top + screen_size_y(s) - 1;
853: if (data->selected > items - 1)
854: data->selected = items - 1;
855: window_choose_redraw_screen(wp);
856: break;
857: case KEYC_END:
858: case '>'|KEYC_ESCAPE:
859: data->selected = items - 1;
860: if (screen_size_y(s) < items)
861: data->top = items - screen_size_y(s);
862: else
863: data->top = 0;
864: window_choose_redraw_screen(wp);
865: break;
866: default:
867: idx = window_choose_index_key(wp, key);
868: if (idx < 0 || (u_int) idx >= data->list_size)
869: break;
870: data->selected = idx;
871: window_choose_fire_callback(wp, data->list[idx].wcd);
872: break;
873: }
874: }
875:
876: static void
877: window_choose_write_line(struct window_pane *wp, struct screen_write_ctx *ctx,
878: u_int py)
879: {
880: struct window_choose_mode_data *data = wp->modedata;
881: struct window_choose_mode_item *item;
882: struct options *oo = wp->window->options;
883: struct screen *s = &data->screen;
884: struct grid_cell gc;
885: size_t last, xoff = 0;
886: char hdr[32], label[32];
887: int key;
888:
889: if (data->callbackfn == NULL)
890: fatalx("called before callback assigned");
891:
892: last = screen_size_y(s) - 1;
893: memcpy(&gc, &grid_default_cell, sizeof gc);
894: gc.flags |= GRID_FLAG_NOPALETTE;
895: if (data->selected == data->top + py)
896: style_apply(&gc, oo, "mode-style");
897:
898: screen_write_cursormove(ctx, 0, py);
899: if (data->top + py < data->list_size) {
900: item = &data->list[data->top + py];
901: if (item->wcd->wl != NULL &&
902: item->wcd->wl->flags & WINLINK_ALERTFLAGS)
903: gc.attr |= GRID_ATTR_BRIGHT;
904:
905: key = window_choose_key_index(wp, data->top + py);
906: if (key != -1)
907: xsnprintf(label, sizeof label, "(%c)", key);
908: else
909: xsnprintf(label, sizeof label, "(%d)", item->pos);
910: screen_write_nputs(ctx, screen_size_x(s) - 1, &gc,
911: "%*s %s %s", data->width + 2, label,
912: /*
913: * Add indication to tree if necessary about whether it's
914: * expanded or not.
915: */
916: (item->wcd->type & TREE_SESSION) ?
917: ((item->state & TREE_EXPANDED) ? "-" : "+") : "", item->name);
918: }
919: while (s->cx < screen_size_x(s) - 1)
920: screen_write_putc(ctx, &gc, ' ');
921:
922: if (data->input_type != WINDOW_CHOOSE_NORMAL) {
923: style_apply(&gc, oo, "mode-style");
924:
925: xoff = xsnprintf(hdr, sizeof hdr,
926: "%s: %s", data->input_prompt, data->input_str);
927: screen_write_cursormove(ctx, 0, last);
928: screen_write_puts(ctx, &gc, "%s", hdr);
929: screen_write_cursormove(ctx, xoff, py);
930: memcpy(&gc, &grid_default_cell, sizeof gc);
931: }
932:
933: }
934:
935: static int
936: window_choose_key_index(struct window_pane *wp, u_int idx)
937: {
938: const char *ptr;
939: int keys;
940:
941: keys = options_get_number(wp->window->options, "mode-keys");
942: if (keys == MODEKEY_VI)
943: ptr = window_choose_keys_vi;
944: else
945: ptr = window_choose_keys_emacs;
946: for (; *ptr != '\0'; ptr++) {
947: if (idx-- == 0)
948: return (*ptr);
949: }
950: return (-1);
951: }
952:
953: static int
954: window_choose_index_key(struct window_pane *wp, key_code key)
955: {
956: const char *ptr;
957: int keys;
958: u_int idx = 0;
959:
960: keys = options_get_number(wp->window->options, "mode-keys");
961: if (keys == MODEKEY_VI)
962: ptr = window_choose_keys_vi;
963: else
964: ptr = window_choose_keys_emacs;
965: for (; *ptr != '\0'; ptr++) {
966: if (key == (key_code)*ptr)
967: return (idx);
968: idx++;
969: }
970: return (-1);
971: }
972:
973: static void
974: window_choose_redraw_screen(struct window_pane *wp)
975: {
976: struct window_choose_mode_data *data = wp->modedata;
977: struct screen *s = &data->screen;
978: struct screen_write_ctx ctx;
979: u_int i;
980:
981: screen_write_start(&ctx, wp, NULL);
982: for (i = 0; i < screen_size_y(s); i++)
983: window_choose_write_line(wp, &ctx, i);
984: screen_write_stop(&ctx);
985: }
986:
987: static void
988: window_choose_scroll_up(struct window_pane *wp)
989: {
990: struct window_choose_mode_data *data = wp->modedata;
991: struct screen_write_ctx ctx;
992:
993: if (data->top == 0)
994: return;
995: data->top--;
996:
997: screen_write_start(&ctx, wp, NULL);
998: screen_write_cursormove(&ctx, 0, 0);
999: screen_write_insertline(&ctx, 1, 8);
1000: window_choose_write_line(wp, &ctx, 0);
1001: if (screen_size_y(&data->screen) > 1)
1002: window_choose_write_line(wp, &ctx, 1);
1003: screen_write_stop(&ctx);
1004: }
1005:
1006: static void
1007: window_choose_scroll_down(struct window_pane *wp)
1008: {
1009: struct window_choose_mode_data *data = wp->modedata;
1010: struct screen *s = &data->screen;
1011: struct screen_write_ctx ctx;
1012:
1013: if (data->top >= data->list_size)
1014: return;
1015: data->top++;
1016:
1017: screen_write_start(&ctx, wp, NULL);
1018: screen_write_cursormove(&ctx, 0, 0);
1019: screen_write_deleteline(&ctx, 1, 8);
1020: window_choose_write_line(wp, &ctx, screen_size_y(s) - 1);
1021: if (screen_size_y(&data->screen) > 1)
1022: window_choose_write_line(wp, &ctx, screen_size_y(s) - 2);
1023: screen_write_stop(&ctx);
1024: }
1025:
1026: struct window_choose_data *
1027: window_choose_add_session(struct window_pane *wp, struct client *c,
1028: struct session *s, const char *template, const char *action, u_int idx)
1029: {
1030: struct window_choose_data *wcd;
1031:
1032: wcd = window_choose_data_create(TREE_SESSION, c, c->session);
1033: wcd->idx = s->id;
1034:
1035: wcd->tree_session = s;
1036: wcd->tree_session->references++;
1037:
1038: wcd->ft_template = xstrdup(template);
1039: format_add(wcd->ft, "line", "%u", idx);
1040: format_defaults(wcd->ft, NULL, s, NULL, NULL);
1041:
1042: wcd->command = cmd_template_replace(action, s->name, 1);
1043:
1044: window_choose_add(wp, wcd);
1045:
1046: return (wcd);
1047: }
1048:
1049: struct window_choose_data *
1050: window_choose_add_window(struct window_pane *wp, struct client *c,
1051: struct session *s, struct winlink *wl, const char *template,
1052: const char *action, u_int idx)
1053: {
1054: struct window_choose_data *wcd;
1055: char *expanded;
1056:
1057: wcd = window_choose_data_create(TREE_WINDOW, c, c->session);
1058: wcd->idx = wl->idx;
1059:
1060: wcd->wl = wl;
1061:
1062: wcd->tree_session = s;
1063: wcd->tree_session->references++;
1064:
1065: wcd->ft_template = xstrdup(template);
1066: format_add(wcd->ft, "line", "%u", idx);
1067: format_defaults(wcd->ft, NULL, s, wl, NULL);
1068:
1069: xasprintf(&expanded, "%s:%d", s->name, wl->idx);
1070: wcd->command = cmd_template_replace(action, expanded, 1);
1071: free(expanded);
1072:
1073: window_choose_add(wp, wcd);
1074:
1075: return (wcd);
1076: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>