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