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