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