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