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