PageRenderTime 57ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/menu.c

https://git.sr.ht/~kevin8t8/mutt/
C | 1253 lines | 1051 code | 147 blank | 55 comment | 225 complexity | 3fd9bb582fcc121fc5ee90bddd432da1 MD5 | raw file
Possible License(s): AGPL-1.0
  1. /*
  2. * Copyright (C) 1996-2000,2002,2012 Michael R. Elkins <me@mutt.org>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. */
  18. #if HAVE_CONFIG_H
  19. # include "config.h"
  20. #endif
  21. #include "mutt.h"
  22. #include "mutt_curses.h"
  23. #include "mutt_menu.h"
  24. #include "mbyte.h"
  25. #ifdef USE_SIDEBAR
  26. #include "sidebar.h"
  27. #endif
  28. char* SearchBuffers[MENU_MAX];
  29. /* These are used to track the active menus, for redraw operations. */
  30. static size_t MenuStackCount = 0;
  31. static size_t MenuStackLen = 0;
  32. static MUTTMENU **MenuStack = NULL;
  33. static void print_enriched_string (int attr, unsigned char *s, int do_color)
  34. {
  35. wchar_t wc;
  36. size_t k;
  37. size_t n = mutt_strlen ((char *)s);
  38. mbstate_t mbstate;
  39. memset (&mbstate, 0, sizeof (mbstate));
  40. while (*s)
  41. {
  42. if (*s < MUTT_TREE_MAX)
  43. {
  44. if (do_color)
  45. SETCOLOR (MT_COLOR_TREE);
  46. while (*s && *s < MUTT_TREE_MAX)
  47. {
  48. switch (*s)
  49. {
  50. case MUTT_TREE_LLCORNER:
  51. if (option (OPTASCIICHARS))
  52. addch ('`');
  53. #ifdef WACS_LLCORNER
  54. else
  55. add_wch(WACS_LLCORNER);
  56. #else
  57. else if (Charset_is_utf8)
  58. addstr ("\342\224\224"); /* WACS_LLCORNER */
  59. else
  60. addch (ACS_LLCORNER);
  61. #endif
  62. break;
  63. case MUTT_TREE_ULCORNER:
  64. if (option (OPTASCIICHARS))
  65. addch (',');
  66. #ifdef WACS_ULCORNER
  67. else
  68. add_wch(WACS_ULCORNER);
  69. #else
  70. else if (Charset_is_utf8)
  71. addstr ("\342\224\214"); /* WACS_ULCORNER */
  72. else
  73. addch (ACS_ULCORNER);
  74. #endif
  75. break;
  76. case MUTT_TREE_LTEE:
  77. if (option (OPTASCIICHARS))
  78. addch ('|');
  79. #ifdef WACS_LTEE
  80. else
  81. add_wch(WACS_LTEE);
  82. #else
  83. else if (Charset_is_utf8)
  84. addstr ("\342\224\234"); /* WACS_LTEE */
  85. else
  86. addch (ACS_LTEE);
  87. #endif
  88. break;
  89. case MUTT_TREE_HLINE:
  90. if (option (OPTASCIICHARS))
  91. addch ('-');
  92. #ifdef WACS_HLINE
  93. else
  94. add_wch(WACS_HLINE);
  95. #else
  96. else if (Charset_is_utf8)
  97. addstr ("\342\224\200"); /* WACS_HLINE */
  98. else
  99. addch (ACS_HLINE);
  100. #endif
  101. break;
  102. case MUTT_TREE_VLINE:
  103. if (option (OPTASCIICHARS))
  104. addch ('|');
  105. #ifdef WACS_VLINE
  106. else
  107. add_wch(WACS_VLINE);
  108. #else
  109. else if (Charset_is_utf8)
  110. addstr ("\342\224\202"); /* WACS_VLINE */
  111. else
  112. addch (ACS_VLINE);
  113. #endif
  114. break;
  115. case MUTT_TREE_TTEE:
  116. if (option (OPTASCIICHARS))
  117. addch ('-');
  118. #ifdef WACS_TTEE
  119. else
  120. add_wch(WACS_TTEE);
  121. #else
  122. else if (Charset_is_utf8)
  123. addstr ("\342\224\254"); /* WACS_TTEE */
  124. else
  125. addch (ACS_TTEE);
  126. #endif
  127. break;
  128. case MUTT_TREE_BTEE:
  129. if (option (OPTASCIICHARS))
  130. addch ('-');
  131. #ifdef WACS_BTEE
  132. else
  133. add_wch(WACS_BTEE);
  134. #else
  135. else if (Charset_is_utf8)
  136. addstr ("\342\224\264"); /* WACS_BTEE */
  137. else
  138. addch (ACS_BTEE);
  139. #endif
  140. break;
  141. case MUTT_TREE_SPACE:
  142. addch (' ');
  143. break;
  144. case MUTT_TREE_RARROW:
  145. addch ('>');
  146. break;
  147. case MUTT_TREE_STAR:
  148. addch ('*'); /* fake thread indicator */
  149. break;
  150. case MUTT_TREE_HIDDEN:
  151. addch ('&');
  152. break;
  153. case MUTT_TREE_EQUALS:
  154. addch ('=');
  155. break;
  156. case MUTT_TREE_MISSING:
  157. addch ('?');
  158. break;
  159. }
  160. s++, n--;
  161. }
  162. if (do_color) ATTRSET(attr);
  163. }
  164. else if ((k = mbrtowc (&wc, (char *)s, n, &mbstate)) > 0)
  165. {
  166. addnstr ((char *)s, k);
  167. s += k, n-= k;
  168. }
  169. else
  170. break;
  171. }
  172. }
  173. static void menu_make_entry (char *s, int l, MUTTMENU *menu, int i)
  174. {
  175. if (menu->dialog)
  176. {
  177. strncpy (s, NONULL (menu->dialog[i]), l);
  178. menu->current = -1; /* hide menubar */
  179. }
  180. else
  181. menu->make_entry (s, l, menu, i);
  182. }
  183. static void menu_pad_string (MUTTMENU *menu, char *s, size_t n)
  184. {
  185. char *scratch = safe_strdup (s);
  186. int shift = option (OPTARROWCURSOR) ? 3 : 0;
  187. int cols = menu->indexwin->cols - shift;
  188. mutt_format_string (s, n, cols, cols, FMT_LEFT, ' ', scratch, mutt_strlen (scratch), 1);
  189. s[n - 1] = 0;
  190. FREE (&scratch);
  191. }
  192. void menu_redraw_full (MUTTMENU *menu)
  193. {
  194. #if ! (defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM))
  195. mutt_reflow_windows ();
  196. #endif
  197. NORMAL_COLOR;
  198. /* clear() doesn't optimize screen redraws */
  199. move (0, 0);
  200. clrtobot ();
  201. if (option (OPTHELP))
  202. {
  203. SETCOLOR (MT_COLOR_STATUS);
  204. mutt_window_move (menu->helpwin, 0, 0);
  205. mutt_paddstr (menu->helpwin->cols, menu->help);
  206. NORMAL_COLOR;
  207. }
  208. menu->offset = 0;
  209. menu->pagelen = menu->indexwin->rows;
  210. mutt_show_error ();
  211. menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
  212. #ifdef USE_SIDEBAR
  213. menu->redraw |= REDRAW_SIDEBAR;
  214. #endif
  215. }
  216. void menu_redraw_status (MUTTMENU *menu)
  217. {
  218. char buf[STRING];
  219. snprintf (buf, sizeof (buf), MUTT_MODEFMT, menu->title);
  220. SETCOLOR (MT_COLOR_STATUS);
  221. mutt_window_move (menu->statuswin, 0, 0);
  222. mutt_paddstr (menu->statuswin->cols, buf);
  223. NORMAL_COLOR;
  224. menu->redraw &= ~REDRAW_STATUS;
  225. }
  226. #ifdef USE_SIDEBAR
  227. void menu_redraw_sidebar (MUTTMENU *menu)
  228. {
  229. menu->redraw &= ~REDRAW_SIDEBAR;
  230. mutt_sb_draw ();
  231. }
  232. #endif
  233. void menu_redraw_index (MUTTMENU *menu)
  234. {
  235. char buf[LONG_STRING];
  236. int i;
  237. int do_color;
  238. int attr;
  239. for (i = menu->top; i < menu->top + menu->pagelen; i++)
  240. {
  241. if (i < menu->max)
  242. {
  243. attr = menu->color(i);
  244. menu_make_entry (buf, sizeof (buf), menu, i);
  245. menu_pad_string (menu, buf, sizeof (buf));
  246. ATTRSET(attr);
  247. mutt_window_move (menu->indexwin, i - menu->top + menu->offset, 0);
  248. do_color = 1;
  249. if (i == menu->current)
  250. {
  251. SETCOLOR(MT_COLOR_INDICATOR);
  252. if (option(OPTARROWCURSOR))
  253. {
  254. addstr ("->");
  255. ATTRSET(attr);
  256. addch (' ');
  257. }
  258. else
  259. do_color = 0;
  260. }
  261. else if (option(OPTARROWCURSOR))
  262. addstr(" ");
  263. print_enriched_string (attr, (unsigned char *) buf, do_color);
  264. }
  265. else
  266. {
  267. NORMAL_COLOR;
  268. mutt_window_clearline (menu->indexwin, i - menu->top + menu->offset);
  269. }
  270. }
  271. NORMAL_COLOR;
  272. menu->redraw = 0;
  273. }
  274. void menu_redraw_motion (MUTTMENU *menu)
  275. {
  276. char buf[LONG_STRING];
  277. int old_color, cur_color;
  278. if (menu->dialog)
  279. {
  280. menu->redraw &= ~REDRAW_MOTION;
  281. return;
  282. }
  283. /* Note: menu->color() for the index can end up retrieving a message
  284. * over imap (if matching against ~h for instance). This can
  285. * generate status messages. So we want to call it *before* we
  286. * position the cursor for drawing. */
  287. old_color = menu->color (menu->oldcurrent);
  288. mutt_window_move (menu->indexwin, menu->oldcurrent + menu->offset - menu->top, 0);
  289. ATTRSET(old_color);
  290. if (option (OPTARROWCURSOR))
  291. {
  292. /* clear the pointer */
  293. addstr (" ");
  294. if (menu->redraw & REDRAW_MOTION_RESYNCH)
  295. {
  296. menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
  297. menu_pad_string (menu, buf, sizeof (buf));
  298. mutt_window_move (menu->indexwin, menu->oldcurrent + menu->offset - menu->top, 3);
  299. print_enriched_string (old_color, (unsigned char *) buf, 1);
  300. }
  301. /* now draw it in the new location */
  302. SETCOLOR(MT_COLOR_INDICATOR);
  303. mutt_window_mvaddstr (menu->indexwin, menu->current + menu->offset - menu->top, 0, "->");
  304. }
  305. else
  306. {
  307. /* erase the current indicator */
  308. menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
  309. menu_pad_string (menu, buf, sizeof (buf));
  310. print_enriched_string (old_color, (unsigned char *) buf, 1);
  311. /* now draw the new one to reflect the change */
  312. cur_color = menu->color (menu->current);
  313. menu_make_entry (buf, sizeof (buf), menu, menu->current);
  314. menu_pad_string (menu, buf, sizeof (buf));
  315. SETCOLOR(MT_COLOR_INDICATOR);
  316. mutt_window_move (menu->indexwin, menu->current + menu->offset - menu->top, 0);
  317. print_enriched_string (cur_color, (unsigned char *) buf, 0);
  318. }
  319. menu->redraw &= REDRAW_STATUS;
  320. NORMAL_COLOR;
  321. }
  322. void menu_redraw_current (MUTTMENU *menu)
  323. {
  324. char buf[LONG_STRING];
  325. int attr = menu->color (menu->current);
  326. mutt_window_move (menu->indexwin, menu->current + menu->offset - menu->top, 0);
  327. menu_make_entry (buf, sizeof (buf), menu, menu->current);
  328. menu_pad_string (menu, buf, sizeof (buf));
  329. SETCOLOR(MT_COLOR_INDICATOR);
  330. if (option (OPTARROWCURSOR))
  331. {
  332. addstr ("->");
  333. ATTRSET(attr);
  334. addch (' ');
  335. menu_pad_string (menu, buf, sizeof (buf));
  336. print_enriched_string (attr, (unsigned char *) buf, 1);
  337. }
  338. else
  339. print_enriched_string (attr, (unsigned char *) buf, 0);
  340. menu->redraw &= REDRAW_STATUS;
  341. NORMAL_COLOR;
  342. }
  343. static void menu_redraw_prompt (MUTTMENU *menu)
  344. {
  345. if (menu->dialog)
  346. {
  347. if (option (OPTMSGERR))
  348. {
  349. mutt_sleep (1);
  350. unset_option (OPTMSGERR);
  351. }
  352. if (*Errorbuf)
  353. mutt_clear_error ();
  354. mutt_window_mvaddstr (menu->messagewin, 0, 0, menu->prompt);
  355. mutt_window_clrtoeol (menu->messagewin);
  356. }
  357. }
  358. void menu_check_recenter (MUTTMENU *menu)
  359. {
  360. int c = MIN (MenuContext, menu->pagelen / 2);
  361. int old_top = menu->top;
  362. if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) /* less entries than lines */
  363. {
  364. if (menu->top != 0)
  365. {
  366. menu->top = 0;
  367. menu->redraw |= REDRAW_INDEX;
  368. }
  369. }
  370. else
  371. {
  372. if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext))
  373. {
  374. if (menu->current < menu->top + c)
  375. menu->top = menu->current - c;
  376. else if (menu->current >= menu->top + menu->pagelen - c)
  377. menu->top = menu->current - menu->pagelen + c + 1;
  378. }
  379. else
  380. {
  381. if (menu->current < menu->top + c)
  382. menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
  383. else if ((menu->current >= menu->top + menu->pagelen - c))
  384. menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
  385. }
  386. }
  387. if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
  388. menu->top = MIN (menu->top, menu->max - menu->pagelen);
  389. menu->top = MAX (menu->top, 0);
  390. if (menu->top != old_top)
  391. menu->redraw |= REDRAW_INDEX;
  392. }
  393. void menu_jump (MUTTMENU *menu)
  394. {
  395. int n;
  396. char buf[SHORT_STRING];
  397. if (menu->max)
  398. {
  399. mutt_unget_event (LastKey, 0);
  400. buf[0] = 0;
  401. if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
  402. {
  403. if (mutt_atoi (buf, &n) == 0 && n > 0 && n < menu->max + 1)
  404. {
  405. n--; /* msg numbers are 0-based */
  406. menu->current = n;
  407. menu->redraw = REDRAW_MOTION;
  408. }
  409. else
  410. mutt_error _("Invalid index number.");
  411. }
  412. }
  413. else
  414. mutt_error _("No entries.");
  415. }
  416. void menu_next_line (MUTTMENU *menu)
  417. {
  418. if (menu->max)
  419. {
  420. int c = MIN (MenuContext, menu->pagelen / 2);
  421. if ((menu->top + 1 < menu->max - c) &&
  422. (option(OPTMENUMOVEOFF) ||
  423. (menu->max > menu->pagelen && menu->top < menu->max - menu->pagelen)))
  424. {
  425. menu->top++;
  426. if (menu->current < menu->top + c && menu->current < menu->max - 1)
  427. menu->current++;
  428. menu->redraw = REDRAW_INDEX;
  429. }
  430. else
  431. mutt_error _("You cannot scroll down farther.");
  432. }
  433. else
  434. mutt_error _("No entries.");
  435. }
  436. void menu_prev_line (MUTTMENU *menu)
  437. {
  438. if (menu->top > 0)
  439. {
  440. int c = MIN (MenuContext, menu->pagelen / 2);
  441. menu->top--;
  442. if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
  443. menu->current--;
  444. menu->redraw = REDRAW_INDEX;
  445. }
  446. else
  447. mutt_error _("You cannot scroll up farther.");
  448. }
  449. /*
  450. * pageup: jumplen == -pagelen
  451. * pagedown: jumplen == pagelen
  452. * halfup: jumplen == -pagelen/2
  453. * halfdown: jumplen == pagelen/2
  454. */
  455. #define DIRECTION ((neg * 2) + 1)
  456. static void menu_length_jump (MUTTMENU *menu, int jumplen)
  457. {
  458. int tmp, neg = (jumplen >= 0) ? 0 : -1;
  459. int c = MIN (MenuContext, menu->pagelen / 2);
  460. if (menu->max)
  461. {
  462. /* possible to scroll? */
  463. if (DIRECTION * menu->top <
  464. (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/))))
  465. {
  466. menu->top += jumplen;
  467. /* jumped too long? */
  468. if ((neg || !option (OPTMENUMOVEOFF)) &&
  469. DIRECTION * menu->top > tmp)
  470. menu->top = tmp;
  471. /* need to move the cursor? */
  472. if ((DIRECTION *
  473. (tmp = (menu->current -
  474. (menu->top + (neg ? (menu->pagelen - 1) - c : c))
  475. ))) < 0)
  476. menu->current -= tmp;
  477. menu->redraw = REDRAW_INDEX;
  478. }
  479. else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog)
  480. {
  481. menu->current += jumplen;
  482. menu->redraw = REDRAW_MOTION;
  483. }
  484. else
  485. mutt_error (neg ? _("You are on the first page.")
  486. : _("You are on the last page."));
  487. menu->current = MIN (menu->current, menu->max - 1);
  488. menu->current = MAX (menu->current, 0);
  489. }
  490. else
  491. mutt_error _("No entries.");
  492. }
  493. #undef DIRECTION
  494. void menu_next_page (MUTTMENU *menu)
  495. {
  496. menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
  497. }
  498. void menu_prev_page (MUTTMENU *menu)
  499. {
  500. menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
  501. }
  502. void menu_half_down (MUTTMENU *menu)
  503. {
  504. menu_length_jump (menu, menu->pagelen / 2);
  505. }
  506. void menu_half_up (MUTTMENU *menu)
  507. {
  508. menu_length_jump (menu, 0 - menu->pagelen / 2);
  509. }
  510. void menu_top_page (MUTTMENU *menu)
  511. {
  512. if (menu->current != menu->top)
  513. {
  514. menu->current = menu->top;
  515. menu->redraw = REDRAW_MOTION;
  516. }
  517. }
  518. void menu_bottom_page (MUTTMENU *menu)
  519. {
  520. if (menu->max)
  521. {
  522. menu->current = menu->top + menu->pagelen - 1;
  523. if (menu->current > menu->max - 1)
  524. menu->current = menu->max - 1;
  525. menu->redraw = REDRAW_MOTION;
  526. }
  527. else
  528. mutt_error _("No entries.");
  529. }
  530. void menu_middle_page (MUTTMENU *menu)
  531. {
  532. int i;
  533. if (menu->max)
  534. {
  535. i = menu->top + menu->pagelen;
  536. if (i > menu->max - 1)
  537. i = menu->max - 1;
  538. menu->current = menu->top + (i - menu->top) / 2;
  539. menu->redraw = REDRAW_MOTION;
  540. }
  541. else
  542. mutt_error _("No entries.");
  543. }
  544. void menu_first_entry (MUTTMENU *menu)
  545. {
  546. if (menu->max)
  547. {
  548. menu->current = 0;
  549. menu->redraw = REDRAW_MOTION;
  550. }
  551. else
  552. mutt_error _("No entries.");
  553. }
  554. void menu_last_entry (MUTTMENU *menu)
  555. {
  556. if (menu->max)
  557. {
  558. menu->current = menu->max - 1;
  559. menu->redraw = REDRAW_MOTION;
  560. }
  561. else
  562. mutt_error _("No entries.");
  563. }
  564. void menu_current_top (MUTTMENU *menu)
  565. {
  566. if (menu->max)
  567. {
  568. menu->top = menu->current;
  569. menu->redraw = REDRAW_INDEX;
  570. }
  571. else
  572. mutt_error _("No entries.");
  573. }
  574. void menu_current_middle (MUTTMENU *menu)
  575. {
  576. if (menu->max)
  577. {
  578. menu->top = menu->current - menu->pagelen / 2;
  579. if (menu->top < 0)
  580. menu->top = 0;
  581. menu->redraw = REDRAW_INDEX;
  582. }
  583. else
  584. mutt_error _("No entries.");
  585. }
  586. void menu_current_bottom (MUTTMENU *menu)
  587. {
  588. if (menu->max)
  589. {
  590. menu->top = menu->current - menu->pagelen + 1;
  591. if (menu->top < 0)
  592. menu->top = 0;
  593. menu->redraw = REDRAW_INDEX;
  594. }
  595. else
  596. mutt_error _("No entries.");
  597. }
  598. static void menu_next_entry (MUTTMENU *menu)
  599. {
  600. if (menu->current < menu->max - 1)
  601. {
  602. menu->current++;
  603. menu->redraw = REDRAW_MOTION;
  604. }
  605. else
  606. mutt_error _("You are on the last entry.");
  607. }
  608. static void menu_prev_entry (MUTTMENU *menu)
  609. {
  610. if (menu->current)
  611. {
  612. menu->current--;
  613. menu->redraw = REDRAW_MOTION;
  614. }
  615. else
  616. mutt_error _("You are on the first entry.");
  617. }
  618. static int default_color (int i)
  619. {
  620. return ColorDefs[MT_COLOR_NORMAL];
  621. }
  622. static int menu_search_generic (MUTTMENU *m, regex_t *re, int n)
  623. {
  624. char buf[LONG_STRING];
  625. menu_make_entry (buf, sizeof (buf), m, n);
  626. return (regexec (re, buf, 0, NULL, 0));
  627. }
  628. void mutt_menu_init (void)
  629. {
  630. int i;
  631. for (i = 0; i < MENU_MAX; i++)
  632. SearchBuffers[i] = NULL;
  633. }
  634. MUTTMENU *mutt_new_menu (int menu)
  635. {
  636. MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
  637. if ((menu < 0) || (menu >= MENU_MAX))
  638. menu = MENU_GENERIC;
  639. p->menu = menu;
  640. p->current = 0;
  641. p->top = 0;
  642. p->offset = 0;
  643. p->redraw = REDRAW_FULL;
  644. p->pagelen = MuttIndexWindow->rows;
  645. p->indexwin = MuttIndexWindow;
  646. p->statuswin = MuttStatusWindow;
  647. p->helpwin = MuttHelpWindow;
  648. p->messagewin = MuttMessageWindow;
  649. p->color = default_color;
  650. p->search = menu_search_generic;
  651. return (p);
  652. }
  653. void mutt_menuDestroy (MUTTMENU **p)
  654. {
  655. int i;
  656. if ((*p)->dialog)
  657. {
  658. for (i=0; i < (*p)->max; i++)
  659. FREE (&(*p)->dialog[i]);
  660. FREE (& (*p)->dialog);
  661. }
  662. FREE (p); /* __FREE_CHECKED__ */
  663. }
  664. void mutt_menu_add_dialog_row (MUTTMENU *m, const char *row)
  665. {
  666. if (m->dsize <= m->max)
  667. {
  668. m->dsize += 10;
  669. safe_realloc (&m->dialog, m->dsize * sizeof (char *));
  670. }
  671. m->dialog[m->max++] = safe_strdup (row);
  672. }
  673. static MUTTMENU *get_current_menu (void)
  674. {
  675. return MenuStackCount ? MenuStack[MenuStackCount - 1] : NULL;
  676. }
  677. void mutt_push_current_menu (MUTTMENU *menu)
  678. {
  679. if (MenuStackCount >= MenuStackLen)
  680. {
  681. MenuStackLen += 5;
  682. safe_realloc (&MenuStack, MenuStackLen * sizeof(MUTTMENU *));
  683. }
  684. MenuStack[MenuStackCount++] = menu;
  685. CurrentMenu = menu->menu;
  686. }
  687. void mutt_pop_current_menu (MUTTMENU *menu)
  688. {
  689. MUTTMENU *prev_menu;
  690. if (!MenuStackCount ||
  691. (MenuStack[MenuStackCount - 1] != menu))
  692. {
  693. dprint (1, (debugfile, "mutt_pop_current_menu() called with inactive menu\n"));
  694. return;
  695. }
  696. MenuStackCount--;
  697. prev_menu = get_current_menu ();
  698. if (prev_menu)
  699. {
  700. CurrentMenu = prev_menu->menu;
  701. prev_menu->redraw = REDRAW_FULL;
  702. }
  703. else
  704. {
  705. CurrentMenu = MENU_MAIN;
  706. /* Clearing when Mutt exits would be an annoying change in
  707. * behavior for those who have disabled alternative screens. The
  708. * option is currently set by autocrypt initialization which mixes
  709. * menus and prompts outside of the normal menu system state.
  710. */
  711. if (option (OPTMENUPOPCLEARSCREEN))
  712. {
  713. move (0, 0);
  714. clrtobot ();
  715. }
  716. }
  717. }
  718. void mutt_set_current_menu_redraw (int redraw)
  719. {
  720. MUTTMENU *current_menu;
  721. current_menu = get_current_menu ();
  722. if (current_menu)
  723. current_menu->redraw |= redraw;
  724. }
  725. void mutt_set_current_menu_redraw_full (void)
  726. {
  727. MUTTMENU *current_menu;
  728. current_menu = get_current_menu ();
  729. if (current_menu)
  730. current_menu->redraw = REDRAW_FULL;
  731. }
  732. void mutt_set_menu_redraw (int menu_type, int redraw)
  733. {
  734. if (CurrentMenu == menu_type)
  735. mutt_set_current_menu_redraw (redraw);
  736. }
  737. void mutt_set_menu_redraw_full (int menu_type)
  738. {
  739. if (CurrentMenu == menu_type)
  740. mutt_set_current_menu_redraw_full ();
  741. }
  742. void mutt_current_menu_redraw ()
  743. {
  744. MUTTMENU *current_menu;
  745. current_menu = get_current_menu ();
  746. if (current_menu)
  747. {
  748. if (menu_redraw (current_menu) == OP_REDRAW)
  749. /* On a REDRAW_FULL with a non-customized redraw, menu_redraw()
  750. * will return OP_REDRAW to give the calling menu-loop a chance to
  751. * customize output.
  752. */
  753. menu_redraw (current_menu);
  754. }
  755. }
  756. #define MUTT_SEARCH_UP 1
  757. #define MUTT_SEARCH_DOWN 2
  758. static int menu_search (MUTTMENU *menu, int op)
  759. {
  760. int r, wrap = 0;
  761. int searchDir;
  762. regex_t re;
  763. char buf[SHORT_STRING];
  764. char* searchBuf = menu->menu >= 0 && menu->menu < MENU_MAX ?
  765. SearchBuffers[menu->menu] : NULL;
  766. if (!(searchBuf && *searchBuf) ||
  767. (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE))
  768. {
  769. strfcpy (buf, searchBuf && *searchBuf ? searchBuf : "", sizeof (buf));
  770. if (mutt_get_field ((op == OP_SEARCH || op == OP_SEARCH_NEXT)
  771. ? _("Search for: ") : _("Reverse search for: "),
  772. buf, sizeof (buf), MUTT_CLEAR) != 0 || !buf[0])
  773. return (-1);
  774. if (menu->menu >= 0 && menu->menu < MENU_MAX)
  775. {
  776. mutt_str_replace (&SearchBuffers[menu->menu], buf);
  777. searchBuf = SearchBuffers[menu->menu];
  778. }
  779. menu->searchDir = (op == OP_SEARCH || op == OP_SEARCH_NEXT) ?
  780. MUTT_SEARCH_DOWN : MUTT_SEARCH_UP;
  781. }
  782. searchDir = (menu->searchDir == MUTT_SEARCH_UP) ? -1 : 1;
  783. if (op == OP_SEARCH_OPPOSITE)
  784. searchDir = -searchDir;
  785. if ((r = REGCOMP (&re, searchBuf, REG_NOSUB | mutt_which_case (searchBuf))) != 0)
  786. {
  787. regerror (r, &re, buf, sizeof (buf));
  788. mutt_error ("%s", buf);
  789. return (-1);
  790. }
  791. r = menu->current + searchDir;
  792. search_next:
  793. if (wrap)
  794. mutt_message (_("Search wrapped to top."));
  795. while (r >= 0 && r < menu->max)
  796. {
  797. if (menu->search (menu, &re, r) == 0)
  798. {
  799. regfree (&re);
  800. return r;
  801. }
  802. r += searchDir;
  803. }
  804. if (option (OPTWRAPSEARCH) && wrap++ == 0)
  805. {
  806. r = searchDir == 1 ? 0 : menu->max - 1;
  807. goto search_next;
  808. }
  809. regfree (&re);
  810. mutt_error _("Not found.");
  811. return (-1);
  812. }
  813. static int menu_dialog_translate_op (int i)
  814. {
  815. switch (i)
  816. {
  817. case OP_NEXT_ENTRY:
  818. return OP_NEXT_LINE;
  819. case OP_PREV_ENTRY:
  820. return OP_PREV_LINE;
  821. case OP_CURRENT_TOP: case OP_FIRST_ENTRY:
  822. return OP_TOP_PAGE;
  823. case OP_CURRENT_BOTTOM: case OP_LAST_ENTRY:
  824. return OP_BOTTOM_PAGE;
  825. case OP_CURRENT_MIDDLE:
  826. return OP_MIDDLE_PAGE;
  827. }
  828. return i;
  829. }
  830. static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
  831. {
  832. event_t ch;
  833. char *p;
  834. do
  835. {
  836. ch = mutt_getch();
  837. } while (ch.ch == -2);
  838. if (ch.ch < 0)
  839. {
  840. *ip = -1;
  841. return 0;
  842. }
  843. if (ch.ch && (p = strchr (menu->keys, ch.ch)))
  844. {
  845. *ip = OP_MAX + (p - menu->keys + 1);
  846. return 0;
  847. }
  848. else
  849. {
  850. mutt_unget_event (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
  851. return -1;
  852. }
  853. }
  854. int menu_redraw (MUTTMENU *menu)
  855. {
  856. if (menu->custom_menu_redraw)
  857. {
  858. menu->custom_menu_redraw (menu);
  859. return OP_NULL;
  860. }
  861. /* See if all or part of the screen needs to be updated. */
  862. if (menu->redraw & REDRAW_FULL)
  863. {
  864. menu_redraw_full (menu);
  865. /* allow the caller to do any local configuration */
  866. return (OP_REDRAW);
  867. }
  868. if (!menu->dialog)
  869. menu_check_recenter (menu);
  870. if (menu->redraw & REDRAW_STATUS)
  871. menu_redraw_status (menu);
  872. #ifdef USE_SIDEBAR
  873. if (menu->redraw & REDRAW_SIDEBAR)
  874. menu_redraw_sidebar (menu);
  875. #endif
  876. if (menu->redraw & REDRAW_INDEX)
  877. menu_redraw_index (menu);
  878. else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
  879. menu_redraw_motion (menu);
  880. else if (menu->redraw == REDRAW_CURRENT)
  881. menu_redraw_current (menu);
  882. if (menu->dialog)
  883. menu_redraw_prompt (menu);
  884. return OP_NULL;
  885. }
  886. int mutt_menuLoop (MUTTMENU *menu)
  887. {
  888. int i = OP_NULL;
  889. FOREVER
  890. {
  891. if (option (OPTMENUCALLER))
  892. {
  893. unset_option (OPTMENUCALLER);
  894. return OP_NULL;
  895. }
  896. /* Clear the tag prefix unless we just started it. Don't clear
  897. * the prefix on a timeout (i==-2), but do clear on an abort (i==-1)
  898. */
  899. if (menu->tagprefix &&
  900. i != OP_TAG_PREFIX && i != OP_TAG_PREFIX_COND && i != -2)
  901. menu->tagprefix = 0;
  902. mutt_curs_set (0);
  903. if (menu->custom_menu_update)
  904. menu->custom_menu_update (menu);
  905. if (menu_redraw (menu) == OP_REDRAW)
  906. return OP_REDRAW;
  907. /* give visual indication that the next command is a tag- command */
  908. if (menu->tagprefix)
  909. {
  910. mutt_window_mvaddstr (menu->messagewin, 0, 0, "tag-");
  911. mutt_window_clrtoeol (menu->messagewin);
  912. }
  913. menu->oldcurrent = menu->current;
  914. /* move the cursor out of the way */
  915. if (option (OPTARROWCURSOR))
  916. mutt_window_move (menu->indexwin, menu->current - menu->top + menu->offset, 2);
  917. else if (option (OPTBRAILLEFRIENDLY))
  918. mutt_window_move (menu->indexwin, menu->current - menu->top + menu->offset, 0);
  919. else
  920. mutt_window_move (menu->indexwin, menu->current - menu->top + menu->offset,
  921. menu->indexwin->cols - 1);
  922. mutt_refresh ();
  923. /* try to catch dialog keys before ops */
  924. if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
  925. return i;
  926. i = km_dokey (menu->menu);
  927. if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
  928. {
  929. if (menu->tagprefix)
  930. {
  931. menu->tagprefix = 0;
  932. mutt_window_clearline (menu->messagewin, 0);
  933. continue;
  934. }
  935. if (menu->tagged)
  936. {
  937. menu->tagprefix = 1;
  938. continue;
  939. }
  940. else if (i == OP_TAG_PREFIX)
  941. {
  942. mutt_error _("No tagged entries.");
  943. i = -1;
  944. }
  945. else /* None tagged, OP_TAG_PREFIX_COND */
  946. {
  947. mutt_flush_macro_to_endcond ();
  948. mutt_message _("Nothing to do.");
  949. i = -1;
  950. }
  951. }
  952. else if (menu->tagged && option (OPTAUTOTAG))
  953. menu->tagprefix = 1;
  954. mutt_curs_set (1);
  955. #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
  956. if (SigWinch)
  957. {
  958. SigWinch = 0;
  959. mutt_resize_screen ();
  960. clearok(stdscr,TRUE);/*force complete redraw*/
  961. }
  962. #endif
  963. if (i < 0)
  964. {
  965. if (menu->tagprefix)
  966. mutt_window_clearline (menu->messagewin, 0);
  967. continue;
  968. }
  969. if (!menu->dialog)
  970. mutt_clear_error ();
  971. /* Convert menubar movement to scrolling */
  972. if (menu->dialog)
  973. i = menu_dialog_translate_op (i);
  974. switch (i)
  975. {
  976. case OP_NEXT_ENTRY:
  977. menu_next_entry (menu);
  978. break;
  979. case OP_PREV_ENTRY:
  980. menu_prev_entry (menu);
  981. break;
  982. case OP_HALF_DOWN:
  983. menu_half_down (menu);
  984. break;
  985. case OP_HALF_UP:
  986. menu_half_up (menu);
  987. break;
  988. case OP_NEXT_PAGE:
  989. menu_next_page (menu);
  990. break;
  991. case OP_PREV_PAGE:
  992. menu_prev_page (menu);
  993. break;
  994. case OP_NEXT_LINE:
  995. menu_next_line (menu);
  996. break;
  997. case OP_PREV_LINE:
  998. menu_prev_line (menu);
  999. break;
  1000. case OP_FIRST_ENTRY:
  1001. menu_first_entry (menu);
  1002. break;
  1003. case OP_LAST_ENTRY:
  1004. menu_last_entry (menu);
  1005. break;
  1006. case OP_TOP_PAGE:
  1007. menu_top_page (menu);
  1008. break;
  1009. case OP_MIDDLE_PAGE:
  1010. menu_middle_page (menu);
  1011. break;
  1012. case OP_BOTTOM_PAGE:
  1013. menu_bottom_page (menu);
  1014. break;
  1015. case OP_CURRENT_TOP:
  1016. menu_current_top (menu);
  1017. break;
  1018. case OP_CURRENT_MIDDLE:
  1019. menu_current_middle (menu);
  1020. break;
  1021. case OP_CURRENT_BOTTOM:
  1022. menu_current_bottom (menu);
  1023. break;
  1024. case OP_SEARCH:
  1025. case OP_SEARCH_REVERSE:
  1026. case OP_SEARCH_NEXT:
  1027. case OP_SEARCH_OPPOSITE:
  1028. if (menu->search && !menu->dialog) /* Searching dialogs won't work */
  1029. {
  1030. menu->oldcurrent = menu->current;
  1031. if ((menu->current = menu_search (menu, i)) != -1)
  1032. menu->redraw = REDRAW_MOTION;
  1033. else
  1034. menu->current = menu->oldcurrent;
  1035. }
  1036. else
  1037. mutt_error _("Search is not implemented for this menu.");
  1038. break;
  1039. case OP_JUMP:
  1040. if (menu->dialog)
  1041. mutt_error _("Jumping is not implemented for dialogs.");
  1042. else
  1043. menu_jump (menu);
  1044. break;
  1045. case OP_ENTER_COMMAND:
  1046. mutt_enter_command ();
  1047. break;
  1048. case OP_TAG:
  1049. if (menu->tag && !menu->dialog)
  1050. {
  1051. if (menu->tagprefix && !option (OPTAUTOTAG))
  1052. {
  1053. for (i = 0; i < menu->max; i++)
  1054. menu->tagged += menu->tag (menu, i, 0);
  1055. menu->redraw |= REDRAW_INDEX;
  1056. }
  1057. else if (menu->max)
  1058. {
  1059. int i = menu->tag (menu, menu->current, -1);
  1060. menu->tagged += i;
  1061. if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
  1062. {
  1063. menu->current++;
  1064. menu->redraw |= REDRAW_MOTION_RESYNCH;
  1065. }
  1066. else
  1067. menu->redraw |= REDRAW_CURRENT;
  1068. }
  1069. else
  1070. mutt_error _("No entries.");
  1071. }
  1072. else
  1073. mutt_error _("Tagging is not supported.");
  1074. break;
  1075. case OP_SHELL_ESCAPE:
  1076. mutt_shell_escape ();
  1077. break;
  1078. case OP_WHAT_KEY:
  1079. mutt_what_key ();
  1080. break;
  1081. case OP_CHECK_STATS:
  1082. mutt_check_stats ();
  1083. break;
  1084. case OP_REDRAW:
  1085. clearok (stdscr, TRUE);
  1086. menu->redraw = REDRAW_FULL;
  1087. break;
  1088. case OP_HELP:
  1089. mutt_help (menu->menu);
  1090. menu->redraw = REDRAW_FULL;
  1091. break;
  1092. case OP_ERROR_HISTORY:
  1093. mutt_error_history_display ();
  1094. menu->redraw = REDRAW_FULL;
  1095. break;
  1096. case OP_NULL:
  1097. km_error_key (menu->menu);
  1098. break;
  1099. case OP_END_COND:
  1100. break;
  1101. default:
  1102. return (i);
  1103. }
  1104. }
  1105. /* not reached */
  1106. }