Annotation of embedaddon/tmux/window-choose.c, revision 1.1
1.1 ! misho 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_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_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>