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