window-choose.c 24 KB

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