File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / tmux / window-choose.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 14 12:22:44 2017 UTC (6 years, 11 months ago) by misho
Branches: tmux, MAIN
CVS tags: v2_4p0, v2_4, HEAD
tmux 2.4

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>