NeoMutt  2025-12-11-911-gd8d604
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
functions.c
Go to the documentation of this file.
1
22
28
29#include "config.h"
30#include <inttypes.h>
31#include <stdbool.h>
32#include <stdio.h>
33#include <sys/stat.h>
34#include "mutt/lib.h"
35#include "config/lib.h"
36#include "email/lib.h"
37#include "core/lib.h"
38#include "gui/lib.h"
39#include "mutt.h"
40#include "functions.h"
41#include "lib.h"
42#include "attach/lib.h"
43#include "browser/lib.h"
44#include "color/lib.h"
45#include "editor/lib.h"
46#include "history/lib.h"
47#include "index/lib.h"
48#include "key/lib.h"
49#include "menu/lib.h"
50#include "pattern/lib.h"
51#include "sidebar/lib.h"
52#include "display.h"
53#include "module_data.h"
54#include "muttlib.h"
55#include "private_data.h"
56
58static const char *Not_available_in_this_menu = N_("Not available in this menu");
59
60static int op_pager_search_next(struct PagerFunctionData *fdata, const struct KeyEvent *event);
61
62// clang-format off
66static const struct MenuFuncOp OpPager[] = { /* map: pager */
67 { "bottom", OP_PAGER_BOTTOM },
68 { "bounce-message", OP_BOUNCE_MESSAGE },
69 { "break-thread", OP_MAIN_BREAK_THREAD },
70 { "change-folder", OP_MAIN_CHANGE_FOLDER },
71 { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY },
72 { "change-newsgroup", OP_MAIN_CHANGE_GROUP },
73 { "change-newsgroup-readonly", OP_MAIN_CHANGE_GROUP_READONLY },
74#ifdef USE_NOTMUCH
75 { "change-vfolder", OP_MAIN_CHANGE_VFOLDER },
76#endif
77 { "check-stats", OP_CHECK_STATS },
78 { "check-traditional-pgp", OP_CHECK_TRADITIONAL },
79 { "clear-flag", OP_MAIN_CLEAR_FLAG },
80 { "compose-to-sender", OP_COMPOSE_TO_SENDER },
81 { "copy-message", OP_COPY_MESSAGE },
82 { "create-alias", OP_CREATE_ALIAS },
83 { "decode-copy", OP_DECODE_COPY },
84 { "decode-save", OP_DECODE_SAVE },
85 { "decrypt-copy", OP_DECRYPT_COPY },
86 { "decrypt-save", OP_DECRYPT_SAVE },
87 { "delete-message", OP_DELETE },
88 { "delete-subthread", OP_DELETE_SUBTHREAD },
89 { "delete-thread", OP_DELETE_THREAD },
90 { "display-address", OP_DISPLAY_ADDRESS },
91 { "display-toggle-weed", OP_DISPLAY_HEADERS },
92 { "edit", OP_EDIT_RAW_MESSAGE },
93 { "edit-label", OP_EDIT_LABEL },
94 { "edit-or-view-raw-message", OP_EDIT_OR_VIEW_RAW_MESSAGE },
95 { "edit-raw-message", OP_EDIT_RAW_MESSAGE },
96 { "edit-type", OP_ATTACH_EDIT_TYPE },
97 { "enter-command", OP_ENTER_COMMAND },
98#ifdef USE_NOTMUCH
99 { "entire-thread", OP_MAIN_ENTIRE_THREAD },
100#endif
101 { "exit", OP_EXIT },
102 { "extract-keys", OP_EXTRACT_KEYS },
103 { "flag-message", OP_FLAG_MESSAGE },
104 { "followup-message", OP_FOLLOWUP },
105 { "forget-passphrase", OP_FORGET_PASSPHRASE },
106 { "forward-message", OP_FORWARD_MESSAGE },
107 { "forward-to-group", OP_FORWARD_TO_GROUP },
108 { "group-chat-reply", OP_GROUP_CHAT_REPLY },
109 { "group-reply", OP_GROUP_REPLY },
110 { "half-down", OP_HALF_DOWN },
111 { "half-up", OP_HALF_UP },
112 { "help", OP_HELP },
113 { "imap-fetch-mail", OP_MAIN_IMAP_FETCH },
114 { "imap-logout-all", OP_MAIN_IMAP_LOGOUT_ALL },
115 { "jump", OP_JUMP },
116 { "link-threads", OP_MAIN_LINK_THREADS },
117 { "list-action", OP_LIST_ACTION },
118 { "list-reply", OP_LIST_REPLY },
119 { "list-subscribe", OP_LIST_SUBSCRIBE },
120 { "list-unsubscribe", OP_LIST_UNSUBSCRIBE },
121 { "mail", OP_MAIL },
122 { "mail-key", OP_MAIL_KEY },
123 { "mailbox-list", OP_MAILBOX_LIST },
124 { "mark-as-new", OP_TOGGLE_NEW },
125 { "modify-labels", OP_MAIN_MODIFY_TAGS },
126 { "modify-labels-then-hide", OP_MAIN_MODIFY_TAGS_THEN_HIDE },
127 { "modify-tags", OP_MAIN_MODIFY_TAGS },
128 { "modify-tags-then-hide", OP_MAIN_MODIFY_TAGS_THEN_HIDE },
129 { "next-entry", OP_NEXT_ENTRY },
130 { "next-line", OP_NEXT_LINE },
131 { "next-new", OP_MAIN_NEXT_NEW },
132 { "next-new-then-unread", OP_MAIN_NEXT_NEW_THEN_UNREAD },
133 { "next-page", OP_NEXT_PAGE },
134 { "next-subthread", OP_MAIN_NEXT_SUBTHREAD },
135 { "next-thread", OP_MAIN_NEXT_THREAD },
136 { "next-undeleted", OP_MAIN_NEXT_UNDELETED },
137 { "next-unread", OP_MAIN_NEXT_UNREAD },
138 { "next-unread-mailbox", OP_MAIN_NEXT_UNREAD_MAILBOX },
139 { "parent-message", OP_MAIN_PARENT_MESSAGE },
140 { "pipe-entry", OP_PIPE },
141 { "pipe-message", OP_PIPE },
142 { "post-message", OP_POST },
143 { "previous-entry", OP_PREV_ENTRY },
144 { "previous-line", OP_PREV_LINE },
145 { "previous-new", OP_MAIN_PREV_NEW },
146 { "previous-new-then-unread", OP_MAIN_PREV_NEW_THEN_UNREAD },
147 { "previous-page", OP_PREV_PAGE },
148 { "previous-subthread", OP_MAIN_PREV_SUBTHREAD },
149 { "previous-thread", OP_MAIN_PREV_THREAD },
150 { "previous-undeleted", OP_MAIN_PREV_UNDELETED },
151 { "previous-unread", OP_MAIN_PREV_UNREAD },
152 { "previous-unread-mailbox", OP_MAIN_PREV_UNREAD_MAILBOX },
153 { "print-entry", OP_ATTACH_PRINT },
154 { "print-message", OP_PRINT },
155 { "purge-message", OP_PURGE_MESSAGE },
156 { "purge-thread", OP_PURGE_THREAD },
157 { "quasi-delete", OP_MAIN_QUASI_DELETE },
158 { "quit", OP_QUIT },
159 { "read-subthread", OP_MAIN_READ_SUBTHREAD },
160 { "read-thread", OP_MAIN_READ_THREAD },
161 { "recall-message", OP_RECALL_MESSAGE },
162 { "reconstruct-thread", OP_RECONSTRUCT_THREAD },
163 { "redraw-screen", OP_REDRAW },
164 { "reply", OP_REPLY },
165 { "resend-message", OP_RESEND },
166 { "root-message", OP_MAIN_ROOT_MESSAGE },
167 { "save-entry", OP_ATTACH_SAVE },
168 { "save-message", OP_SAVE },
169 { "search", OP_SEARCH },
170 { "search-next", OP_SEARCH_NEXT },
171 { "search-opposite", OP_SEARCH_OPPOSITE },
172 { "search-reverse", OP_SEARCH_REVERSE },
173 { "search-toggle", OP_SEARCH_TOGGLE },
174 { "set-flag", OP_MAIN_SET_FLAG },
175 { "shell-escape", OP_SHELL_ESCAPE },
176 { "show-log-messages", OP_SHOW_LOG_MESSAGES },
177 { "show-version", OP_VERSION },
178 { "skip-headers", OP_PAGER_SKIP_HEADERS },
179 { "skip-quoted", OP_PAGER_SKIP_QUOTED },
180 { "sort-mailbox", OP_SORT },
181 { "sort-reverse", OP_SORT_REVERSE },
182 { "sync-mailbox", OP_MAIN_SYNC_FOLDER },
183 { "tag-message", OP_TAG },
184 { "toggle-quoted", OP_PAGER_HIDE_QUOTED },
185 { "toggle-write", OP_TOGGLE_WRITE },
186 { "top", OP_PAGER_TOP },
187 { "undelete-message", OP_UNDELETE },
188 { "undelete-subthread", OP_UNDELETE_SUBTHREAD },
189 { "undelete-thread", OP_UNDELETE_THREAD },
190#ifdef USE_NOTMUCH
191 { "vfolder-from-query", OP_MAIN_VFOLDER_FROM_QUERY },
192 { "vfolder-from-query-readonly", OP_MAIN_VFOLDER_FROM_QUERY_READONLY },
193#endif
194 { "view-attachments", OP_VIEW_ATTACHMENTS },
195 { "view-raw-message", OP_VIEW_RAW_MESSAGE },
196 { "what-key", OP_WHAT_KEY },
197 // Deprecated
198 { "buffy-list", OP_MAILBOX_LIST, MFF_DEPRECATED },
199 { "error-history", OP_SHOW_LOG_MESSAGES, MFF_DEPRECATED },
200 { NULL, 0 },
201};
202
206static const struct MenuOpSeq PagerDefaultBindings[] = { /* map: pager */
207 { OP_ATTACH_EDIT_TYPE, "\005" }, // <Ctrl-E>
208 { OP_BOUNCE_MESSAGE, "b" },
209 { OP_CHECK_TRADITIONAL, "\033P" }, // <Alt-P>
210 { OP_COPY_MESSAGE, "C" },
211 { OP_CREATE_ALIAS, "a" },
212 { OP_DECODE_COPY, "\033C" }, // <Alt-C>
213 { OP_DECODE_SAVE, "\033s" }, // <Alt-s>
214 { OP_DELETE, "d" },
215 { OP_DELETE_SUBTHREAD, "\033d" }, // <Alt-d>
216 { OP_DELETE_THREAD, "\004" }, // <Ctrl-D>
217 { OP_DISPLAY_ADDRESS, "@" },
218 { OP_DISPLAY_HEADERS, "h" },
219 { OP_EDIT_LABEL, "Y" },
220 { OP_EDIT_OR_VIEW_RAW_MESSAGE, "e" },
221 { OP_ENTER_COMMAND, ":" },
222 { OP_EXIT, "q" },
223 { OP_EXIT, "x" },
224 { OP_EXTRACT_KEYS, "\013" }, // <Ctrl-K>
225 { OP_FLAG_MESSAGE, "F" },
226 { OP_FORGET_PASSPHRASE, "\006" }, // <Ctrl-F>
227 { OP_FORWARD_MESSAGE, "f" },
228 { OP_GROUP_REPLY, "g" },
229 { OP_HELP, "?" },
230 { OP_LIST_ACTION, "\033L" }, // <Alt-L>
231 { OP_LIST_REPLY, "L" },
232 { OP_MAIL, "m" },
233 { OP_MAILBOX_LIST, "." },
234 { OP_MAIL_KEY, "\033k" }, // <Alt-k>
235 { OP_MAIN_BREAK_THREAD, "#" },
236 { OP_MAIN_CHANGE_FOLDER, "c" },
237 { OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" }, // <Alt-c>
238 { OP_MAIN_CLEAR_FLAG, "W" },
239 { OP_MAIN_LINK_THREADS, "&" },
240 { OP_MAIN_NEXT_NEW_THEN_UNREAD, "\t" }, // <Tab>
241 { OP_MAIN_NEXT_SUBTHREAD, "\033n" }, // <Alt-n>
242 { OP_MAIN_NEXT_THREAD, "\016" }, // <Ctrl-N>
243 { OP_MAIN_NEXT_UNDELETED, "<down>" },
244 { OP_MAIN_NEXT_UNDELETED, "<right>" },
245 { OP_MAIN_NEXT_UNDELETED, "j" },
246 { OP_MAIN_PARENT_MESSAGE, "P" },
247 { OP_MAIN_PREV_SUBTHREAD, "\033p" }, // <Alt-p>
248 { OP_MAIN_PREV_THREAD, "\020" }, // <Ctrl-P>
249 { OP_MAIN_PREV_UNDELETED, "<left>" },
250 { OP_MAIN_PREV_UNDELETED, "<up>" },
251 { OP_MAIN_PREV_UNDELETED, "k" },
252 { OP_MAIN_READ_SUBTHREAD, "\033r" }, // <Alt-r>
253 { OP_MAIN_READ_THREAD, "\022" }, // <Ctrl-R>
254 { OP_MAIN_SET_FLAG, "w" },
255 { OP_MAIN_SYNC_FOLDER, "$" },
256 { OP_NEXT_ENTRY, "J" },
257 { OP_NEXT_LINE, "<keypadenter>" },
258 { OP_NEXT_LINE, "\n" }, // <Enter>
259 { OP_NEXT_LINE, "\r" }, // <Return>
260 { OP_NEXT_PAGE, " " }, // <Space>
261 { OP_NEXT_PAGE, "<pagedown>" },
262 { OP_PAGER_BOTTOM, "<end>" },
263 { OP_PAGER_HIDE_QUOTED, "T" },
264 { OP_PAGER_SKIP_HEADERS, "H" },
265 { OP_PAGER_SKIP_QUOTED, "S" },
266 { OP_PAGER_TOP, "<home>" },
267 { OP_PAGER_TOP, "^" },
268 { OP_PIPE, "|" },
269 { OP_PREV_ENTRY, "K" },
270 { OP_PREV_LINE, "<backspace>" },
271 { OP_PREV_PAGE, "-" },
272 { OP_PREV_PAGE, "<pageup>" },
273 { OP_PRINT, "p" },
274 { OP_QUIT, "Q" },
275 { OP_RECALL_MESSAGE, "R" },
276 { OP_REDRAW, "\014" }, // <Ctrl-L>
277 { OP_REPLY, "r" },
278 { OP_RESEND, "\033e" }, // <Alt-e>
279 { OP_SAVE, "s" },
280 { OP_SEARCH, "/" },
281 { OP_SEARCH_NEXT, "n" },
282 { OP_SEARCH_REVERSE, "\033/" }, // <Alt-/>
283 { OP_SEARCH_TOGGLE, "\\" }, // <Backslash>
284 { OP_SHELL_ESCAPE, "!" },
285 { OP_SORT, "o" },
286 { OP_SORT_REVERSE, "O" },
287 { OP_TAG, "t" },
288 { OP_TOGGLE_NEW, "N" },
289 { OP_TOGGLE_WRITE, "%" },
290 { OP_UNDELETE, "u" },
291 { OP_UNDELETE_SUBTHREAD, "\033u" }, // <Alt-u>
292 { OP_UNDELETE_THREAD, "\025" }, // <Ctrl-U>
293 { OP_VERSION, "V" },
294 { OP_VIEW_ATTACHMENTS, "v" },
295 { 0, NULL },
296};
297// clang-format on
298
302void pager_init_keys(struct NeoMutt *n, struct SubMenu *sm_generic)
303{
304 struct MenuDefinition *md = NULL;
305 struct SubMenu *sm_pager = NULL;
306 struct SubMenu *sm_sidebar = sidebar_get_submenu();
307
308 sm_pager = km_register_submenu(OpPager);
309 md = km_register_menu(MENU_PAGER, "pager");
310 km_menu_add_submenu(md, sm_pager);
311 km_menu_add_submenu(md, sm_sidebar);
313
315 ASSERT(mod_data);
316 mod_data->menu_pager = md;
317}
318
327static inline bool assert_pager_mode(bool test)
328{
329 if (test)
330 return true;
331
334 return false;
335}
336
345static int up_n_lines(int nlines, struct Line *info, int cur, bool hiding)
346{
347 while ((cur > 0) && (nlines > 0))
348 {
349 cur--;
350 if (!hiding || !COLOR_QUOTED(info[cur].cid))
351 nlines--;
352 }
353
354 return cur;
355}
356
366static int down_n_lines(int nlines, struct Line *info, int cur, int max, bool hiding)
367{
368 while ((cur < max) && (nlines > 0))
369 {
370 cur++;
371 if ((cur < max) && (!hiding || !COLOR_QUOTED(info[cur].cid)))
372 nlines--;
373 }
374
375 if (cur > max)
376 cur = max;
377
378 return cur;
379}
380
388bool jump_to_bottom(struct PagerPrivateData *priv, struct PagerView *pview)
389{
390 if (!(priv->lines[priv->cur_line].offset < (priv->st.st_size - 1)))
391 {
392 return false;
393 }
394
395 int line_num = priv->cur_line;
396 /* make sure the types are defined to the end of file */
397 while (display_line(priv->fp, &priv->bytes_read, &priv->lines, line_num,
398 &priv->lines_used, &priv->lines_max,
399 priv->has_types | (pview->flags & MUTT_PAGER_NOWRAP),
400 &priv->quote_list, &priv->q_level, &priv->force_redraw,
401 &priv->search_re, priv->pview->win_pager, &priv->ansi_list) == 0)
402 {
403 line_num++;
404 }
405 priv->top_line = up_n_lines(priv->pview->win_pager->state.rows, priv->lines,
406 priv->lines_used, priv->hide_quoted);
408 return true;
409}
410
411// -----------------------------------------------------------------------------
412
416static int op_pager_bottom(struct PagerFunctionData *fdata, const struct KeyEvent *event)
417{
418 struct PagerPrivateData *priv = fdata->priv;
419 if (!jump_to_bottom(priv, priv->pview))
420 {
421 mutt_message(_("Bottom of message is shown"));
422 return FR_ERROR;
423 }
424
425 return FR_SUCCESS;
426}
427
431static int op_pager_half_down(struct PagerFunctionData *fdata, const struct KeyEvent *event)
432{
433 struct PagerPrivateData *priv = fdata->priv;
434 const bool c_pager_stop = cs_subset_bool(fdata->n->sub, "pager_stop");
435 if (priv->lines[priv->cur_line].offset < (priv->st.st_size - 1))
436 {
437 int rows = priv->pview->win_pager->state.rows;
438 if (event->count > 0)
439 {
440 int advance = event->count * (rows / 2);
441 priv->top_line = down_n_lines(advance, priv->lines, priv->top_line,
442 priv->lines_used, priv->hide_quoted);
443 }
444 else
445 {
446 priv->top_line = up_n_lines(rows / 2, priv->lines, priv->cur_line, priv->hide_quoted);
447 }
449 }
450 else if (c_pager_stop)
451 {
452 /* emulate "less -q" and don't go on to the next message. */
453 if (event->count == 0)
454 {
455 mutt_message(_("Bottom of message is shown"));
456 return FR_ERROR;
457 }
458 }
459 else
460 {
461 /* end of the current message, so display the next message. */
463 }
464 return FR_SUCCESS;
465}
466
470static int op_pager_half_up(struct PagerFunctionData *fdata, const struct KeyEvent *event)
471{
472 struct PagerPrivateData *priv = fdata->priv;
473 const int old_top_line = priv->top_line;
474 if (priv->top_line)
475 {
476 int rows = priv->pview->win_pager->state.rows;
477 int n = MAX(event->count, 1) * (rows / 2 + rows % 2);
478 priv->top_line = up_n_lines(n, priv->lines, priv->top_line, priv->hide_quoted);
480 }
481 else if (event->count == 0)
482 {
483 mutt_message(_("Top of message is shown"));
484 }
485 return (old_top_line == 0) ? FR_ERROR : FR_SUCCESS;
486}
487
491static int op_pager_hide_quoted(struct PagerFunctionData *fdata, const struct KeyEvent *event)
492{
493 struct PagerPrivateData *priv = fdata->priv;
494 if (!priv->has_types)
495 return FR_NO_ACTION;
496
497 priv->hide_quoted ^= MUTT_HIDE;
498 if (priv->hide_quoted && COLOR_QUOTED(priv->lines[priv->top_line].cid))
499 {
500 priv->top_line = up_n_lines(1, priv->lines, priv->top_line, priv->hide_quoted);
501 }
502 else
503 {
505 }
507 return FR_SUCCESS;
508}
509
513static int op_pager_next_line(struct PagerFunctionData *fdata, const struct KeyEvent *event)
514{
515 struct PagerPrivateData *priv = fdata->priv;
516 if (priv->lines[priv->cur_line].offset < (priv->st.st_size - 1))
517 {
518 int n = MAX(event->count, 1);
519 priv->top_line = down_n_lines(n, priv->lines, priv->top_line,
520 priv->lines_used, priv->hide_quoted);
522 }
523 else if (event->count == 0)
524 {
525 mutt_message(_("Bottom of message is shown"));
526 return FR_ERROR;
527 }
528 return FR_SUCCESS;
529}
530
534static int op_pager_next_page(struct PagerFunctionData *fdata, const struct KeyEvent *event)
535{
536 struct PagerPrivateData *priv = fdata->priv;
537 const bool c_pager_stop = cs_subset_bool(fdata->n->sub, "pager_stop");
538 if (priv->lines[priv->cur_line].offset < (priv->st.st_size - 1))
539 {
540 const short c_pager_context = cs_subset_number(fdata->n->sub, "pager_context");
541 if (event->count > 0)
542 {
543 int advance = event->count * (priv->pview->win_pager->state.rows - c_pager_context);
544 priv->top_line = down_n_lines(advance, priv->lines, priv->top_line,
545 priv->lines_used, priv->hide_quoted);
546 }
547 else
548 {
549 priv->top_line = up_n_lines(c_pager_context, priv->lines, priv->cur_line,
550 priv->hide_quoted);
551 }
553 }
554 else if (c_pager_stop)
555 {
556 /* emulate "less -q" and don't go on to the next message. */
557 if (event->count == 0)
558 {
559 mutt_message(_("Bottom of message is shown"));
560 return FR_ERROR;
561 }
562 }
563 else
564 {
565 /* end of the current message, so display the next message. */
567 }
568 return FR_SUCCESS;
569}
570
574static int op_pager_prev_line(struct PagerFunctionData *fdata, const struct KeyEvent *event)
575{
576 struct PagerPrivateData *priv = fdata->priv;
577 if (priv->top_line)
578 {
579 int n = MAX(event->count, 1);
580 priv->top_line = up_n_lines(n, priv->lines, priv->top_line, priv->hide_quoted);
582 }
583 else if (event->count == 0)
584 {
585 mutt_message(_("Top of message is shown"));
586 return FR_ERROR;
587 }
588 return FR_SUCCESS;
589}
590
594static int op_pager_prev_page(struct PagerFunctionData *fdata, const struct KeyEvent *event)
595{
596 struct PagerPrivateData *priv = fdata->priv;
597 if (priv->top_line == 0)
598 {
599 if (event->count == 0)
600 {
601 mutt_message(_("Top of message is shown"));
602 return FR_ERROR;
603 }
604 }
605 else
606 {
607 const short c_pager_context = cs_subset_number(fdata->n->sub, "pager_context");
608 int n = MAX(event->count, 1) * (priv->pview->win_pager->state.rows - c_pager_context);
609 priv->top_line = up_n_lines(n, priv->lines, priv->top_line, priv->hide_quoted);
611 }
612 return FR_SUCCESS;
613}
614
622static int op_pager_search(struct PagerFunctionData *fdata, const struct KeyEvent *event)
623{
624 struct PagerPrivateData *priv = fdata->priv;
625 struct PagerView *pview = priv->pview;
626
627 int rc = FR_NO_ACTION;
628 struct Buffer *buf = buf_pool_get();
629
630 buf_strcpy(buf, priv->search_str);
631 const int op = event->op;
632 if (mw_get_field(((op == OP_SEARCH) || (op == OP_SEARCH_NEXT)) ? _("Search for: ") : _("Reverse search for: "),
634 {
635 goto done;
636 }
637
638 if (mutt_str_equal(buf_string(buf), priv->search_str))
639 {
640 if (priv->search_compiled)
641 {
642 struct KeyEvent event_s = { 0, OP_NULL };
643
644 /* do an implicit search-next */
645 if (op == OP_SEARCH)
646 event_s.op = OP_SEARCH_NEXT;
647 else
648 event_s.op = OP_SEARCH_OPPOSITE;
649
650 priv->wrapped = false;
651 op_pager_search_next(fdata, &event_s);
652 }
653 }
654
655 if (buf_is_empty(buf))
656 goto done;
657
658 mutt_str_copy(priv->search_str, buf_string(buf), sizeof(priv->search_str));
659
660 /* leave search_back alone if op == OP_SEARCH_NEXT */
661 if (op == OP_SEARCH)
662 priv->search_back = false;
663 else if (op == OP_SEARCH_REVERSE)
664 priv->search_back = true;
665
666 if (priv->search_compiled)
667 {
668 regfree(&priv->search_re);
669 for (size_t i = 0; i < priv->lines_used; i++)
670 {
671 FREE(&(priv->lines[i].search));
672 priv->lines[i].search_arr_size = -1;
673 }
674 }
675
676 uint16_t rflags = mutt_mb_is_lower(priv->search_str) ? REG_ICASE : 0;
677 int err = REG_COMP(&priv->search_re, priv->search_str, REG_NEWLINE | rflags);
678 if (err != 0)
679 {
680 regerror(err, &priv->search_re, buf->data, buf->dsize);
681 mutt_error("%s", buf_string(buf));
682 for (size_t i = 0; i < priv->lines_max; i++)
683 {
684 /* cleanup */
685 FREE(&(priv->lines[i].search));
686 priv->lines[i].search_arr_size = -1;
687 }
688 priv->search_flag = 0;
689 priv->search_compiled = false;
690 rc = FR_ERROR;
691 }
692 else
693 {
694 priv->search_compiled = true;
695 /* update the search pointers */
696 int line_num = 0;
697 while (display_line(priv->fp, &priv->bytes_read, &priv->lines, line_num,
698 &priv->lines_used, &priv->lines_max,
699 MUTT_SEARCH | (pview->flags & MUTT_PAGER_NOWRAP) | priv->has_types,
700 &priv->quote_list, &priv->q_level, &priv->force_redraw,
701 &priv->search_re, priv->pview->win_pager, &priv->ansi_list) == 0)
702 {
703 line_num++;
704 }
705
706 if (priv->search_back)
707 {
708 /* searching backward */
709 int i;
710 for (i = priv->top_line; i >= 0; i--)
711 {
712 if ((!priv->hide_quoted || !COLOR_QUOTED(priv->lines[i].cid)) &&
713 !priv->lines[i].cont_line && (priv->lines[i].search_arr_size > 0))
714 {
715 break;
716 }
717 }
718
719 if (i >= 0)
720 priv->top_line = i;
721 }
722 else
723 {
724 /* searching forward */
725 int i;
726 for (i = priv->top_line; i < priv->lines_used; i++)
727 {
728 if ((!priv->hide_quoted || !COLOR_QUOTED(priv->lines[i].cid)) &&
729 !priv->lines[i].cont_line && (priv->lines[i].search_arr_size > 0))
730 {
731 break;
732 }
733 }
734
735 if (i < priv->lines_used)
736 priv->top_line = i;
737 }
738
739 if (priv->lines[priv->top_line].search_arr_size == 0)
740 {
741 priv->search_flag = 0;
742 mutt_error(_("Not found"));
743 rc = FR_ERROR;
744 }
745 else
746 {
747 const short c_search_context = cs_subset_number(fdata->n->sub, "search_context");
748 priv->search_flag = MUTT_SEARCH;
749 /* give some context for search results */
750 if (c_search_context < priv->pview->win_pager->state.rows)
751 priv->searchctx = c_search_context;
752 else
753 priv->searchctx = 0;
754 if (priv->top_line - priv->searchctx > 0)
755 priv->top_line -= priv->searchctx;
756 rc = FR_SUCCESS;
757 }
758 }
761
762done:
763 buf_pool_release(&buf);
764 return rc;
765}
766
774static int op_pager_search_next(struct PagerFunctionData *fdata, const struct KeyEvent *event)
775{
776 struct PagerPrivateData *priv = fdata->priv;
777 if (priv->search_compiled)
778 {
779 const short c_search_context = cs_subset_number(fdata->n->sub, "search_context");
780 bool found = false;
781 priv->wrapped = false;
782
783 if (c_search_context < priv->pview->win_pager->state.rows)
784 priv->searchctx = c_search_context;
785 else
786 priv->searchctx = 0;
787
788 const int op = event->op;
789
790 search_next:
791 if ((!priv->search_back && (op == OP_SEARCH_NEXT)) ||
792 (priv->search_back && (op == OP_SEARCH_OPPOSITE)))
793 {
794 /* searching forward */
795 int i;
796 for (i = priv->wrapped ? 0 : priv->top_line + priv->searchctx + 1;
797 i < priv->lines_used; i++)
798 {
799 if ((!priv->hide_quoted || !COLOR_QUOTED(priv->lines[i].cid)) &&
800 !priv->lines[i].cont_line && (priv->lines[i].search_arr_size > 0))
801 {
802 break;
803 }
804 }
805
806 const bool c_wrap_search = cs_subset_bool(fdata->n->sub, "wrap_search");
807 if (i < priv->lines_used)
808 {
809 priv->top_line = i;
810 found = true;
811 }
812 else if (priv->wrapped || !c_wrap_search)
813 {
814 mutt_error(_("Not found"));
815 }
816 else
817 {
818 mutt_message(_("Search wrapped to top"));
819 priv->wrapped = true;
820 goto search_next;
821 }
822 }
823 else
824 {
825 /* searching backward */
826 int i;
827 for (i = priv->wrapped ? priv->lines_used : priv->top_line + priv->searchctx - 1;
828 i >= 0; i--)
829 {
830 if ((!priv->hide_quoted ||
831 (priv->has_types && !COLOR_QUOTED(priv->lines[i].cid))) &&
832 !priv->lines[i].cont_line && (priv->lines[i].search_arr_size > 0))
833 {
834 break;
835 }
836 }
837
838 const bool c_wrap_search = cs_subset_bool(fdata->n->sub, "wrap_search");
839 if (i >= 0)
840 {
841 priv->top_line = i;
842 found = true;
843 }
844 else if (priv->wrapped || !c_wrap_search)
845 {
846 mutt_error(_("Not found"));
847 }
848 else
849 {
850 mutt_message(_("Search wrapped to bottom"));
851 priv->wrapped = true;
852 goto search_next;
853 }
854 }
855
856 if (!found)
857 return FR_ERROR;
858
859 if (priv->lines[priv->top_line].search_arr_size > 0)
860 {
861 priv->search_flag = MUTT_SEARCH;
862 /* give some context for search results */
863 if (priv->top_line - priv->searchctx > 0)
864 priv->top_line -= priv->searchctx;
865 }
866
868 return FR_SUCCESS;
869 }
870
871 /* no previous search pattern */
872 return op_pager_search(fdata, event);
873}
874
878static int op_pager_skip_headers(struct PagerFunctionData *fdata, const struct KeyEvent *event)
879{
880 struct PagerPrivateData *priv = fdata->priv;
881 struct PagerView *pview = priv->pview;
882
883 if (!priv->has_types)
884 return FR_NO_ACTION;
885
886 int rc = 0;
887 int new_topline = 0;
888
889 while (((new_topline < priv->lines_used) ||
890 (0 == (rc = display_line(priv->fp, &priv->bytes_read, &priv->lines,
891 new_topline, &priv->lines_used, &priv->lines_max,
892 MUTT_TYPES | (pview->flags & MUTT_PAGER_NOWRAP), &priv->quote_list,
893 &priv->q_level, &priv->force_redraw, &priv->search_re,
894 priv->pview->win_pager, &priv->ansi_list)))) &&
895 color_is_header(priv->lines[new_topline].cid))
896 {
897 new_topline++;
898 }
899
900 if (rc < 0)
901 {
902 /* L10N: Displayed if <skip-headers> is invoked in the pager, but
903 there is no text past the headers.
904 (I don't think this is actually possible in Mutt's code, but
905 display some kind of message in case it somehow occurs.) */
906 mutt_warning(_("No text past headers"));
907 return FR_ERROR;
908 }
909 priv->top_line = new_topline;
911 return FR_SUCCESS;
912}
913
921static int pager_skip_quoted_once(struct PagerPrivateData *priv, struct PagerFunctionData *fdata)
922{
923 struct PagerView *pview = priv->pview;
924 const short c_pager_skip_quoted_context = cs_subset_number(fdata->n->sub, "pager_skip_quoted_context");
925 int rc = 0;
926 int new_topline = priv->top_line;
927 int num_quoted = 0;
928
929 /* In a header? Skip all the email headers, and done */
930 if (color_is_header(priv->lines[new_topline].cid))
931 {
932 while (((new_topline < priv->lines_used) ||
933 (0 == (rc = display_line(priv->fp, &priv->bytes_read, &priv->lines,
934 new_topline, &priv->lines_used, &priv->lines_max,
935 MUTT_TYPES | (pview->flags & MUTT_PAGER_NOWRAP), &priv->quote_list,
936 &priv->q_level, &priv->force_redraw, &priv->search_re,
937 priv->pview->win_pager, &priv->ansi_list)))) &&
938 color_is_header(priv->lines[new_topline].cid))
939 {
940 new_topline++;
941 }
942 priv->top_line = new_topline;
944 return 0;
945 }
946
947 /* Already in the body? Skip past previous "context" quoted lines */
948 if (c_pager_skip_quoted_context > 0)
949 {
950 while (((new_topline < priv->lines_used) ||
951 (0 == (rc = display_line(priv->fp, &priv->bytes_read, &priv->lines,
952 new_topline, &priv->lines_used, &priv->lines_max,
953 MUTT_TYPES | (pview->flags & MUTT_PAGER_NOWRAP), &priv->quote_list,
954 &priv->q_level, &priv->force_redraw, &priv->search_re,
955 priv->pview->win_pager, &priv->ansi_list)))) &&
956 COLOR_QUOTED(priv->lines[new_topline].cid))
957 {
958 new_topline++;
959 num_quoted++;
960 }
961
962 if (rc < 0)
963 return -1;
964 }
965
966 if (num_quoted <= c_pager_skip_quoted_context)
967 {
968 num_quoted = 0;
969
970 while (((new_topline < priv->lines_used) ||
971 (0 == (rc = display_line(priv->fp, &priv->bytes_read, &priv->lines,
972 new_topline, &priv->lines_used, &priv->lines_max,
973 MUTT_TYPES | (pview->flags & MUTT_PAGER_NOWRAP), &priv->quote_list,
974 &priv->q_level, &priv->force_redraw, &priv->search_re,
975 priv->pview->win_pager, &priv->ansi_list)))) &&
976 !COLOR_QUOTED(priv->lines[new_topline].cid))
977 {
978 new_topline++;
979 }
980
981 if (rc < 0)
982 return -1;
983
984 while (((new_topline < priv->lines_used) ||
985 (0 == (rc = display_line(priv->fp, &priv->bytes_read, &priv->lines,
986 new_topline, &priv->lines_used, &priv->lines_max,
987 MUTT_TYPES | (pview->flags & MUTT_PAGER_NOWRAP), &priv->quote_list,
988 &priv->q_level, &priv->force_redraw, &priv->search_re,
989 priv->pview->win_pager, &priv->ansi_list)))) &&
990 COLOR_QUOTED(priv->lines[new_topline].cid))
991 {
992 new_topline++;
993 num_quoted++;
994 }
995
996 if (rc < 0)
997 return -1;
998 }
999 priv->top_line = new_topline - MIN(c_pager_skip_quoted_context, num_quoted);
1001 return 0;
1002}
1003
1009static int op_pager_skip_quoted(struct PagerFunctionData *fdata, const struct KeyEvent *event)
1010{
1011 struct PagerPrivateData *priv = fdata->priv;
1012
1013 if (!priv->has_types)
1014 return FR_NO_ACTION;
1015
1016 const int count = MAX(event->count, 1);
1017
1018 for (int i = 0; i < count; i++)
1019 {
1020 if (pager_skip_quoted_once(priv, fdata) < 0)
1021 return FR_NO_ACTION;
1022 }
1023
1024 return FR_SUCCESS;
1025}
1026
1030static int op_pager_top(struct PagerFunctionData *fdata, const struct KeyEvent *event)
1031{
1032 struct PagerPrivateData *priv = fdata->priv;
1033 if (priv->top_line == 0)
1034 {
1035 mutt_message(_("Top of message is shown"));
1036 return FR_ERROR;
1037 }
1038 else
1039 {
1040 priv->top_line = 0;
1042 }
1043
1044 return FR_SUCCESS;
1045}
1046
1047// -----------------------------------------------------------------------------
1048
1052static int op_exit(struct PagerFunctionData *fdata, const struct KeyEvent *event)
1053{
1054 struct PagerPrivateData *priv = fdata->priv;
1055 priv->rc = -1;
1056 priv->loop = PAGER_LOOP_QUIT;
1057 return FR_DONE;
1058}
1059
1063static int op_help(struct PagerFunctionData *fdata, const struct KeyEvent *event)
1064{
1065 struct PagerPrivateData *priv = fdata->priv;
1066 if (priv->pview->mode == PAGER_MODE_HELP)
1067 {
1068 /* don't let the user enter the help-menu from the help screen! */
1069 mutt_error(_("Help is currently being shown"));
1070 return FR_ERROR;
1071 }
1072 mutt_help(fdata->mod_data->menu_pager);
1074 return FR_SUCCESS;
1075}
1076
1080static int op_save(struct PagerFunctionData *fdata, const struct KeyEvent *event)
1081{
1082 struct PagerPrivateData *priv = fdata->priv;
1083 struct PagerView *pview = priv->pview;
1084 if (pview->mode != PAGER_MODE_OTHER)
1085 return FR_UNKNOWN;
1086
1087 if (!priv->fp)
1088 return FR_UNKNOWN;
1089
1090 int rc = FR_ERROR;
1091 FILE *fp_save = NULL;
1092 struct Buffer *buf = buf_pool_get();
1093
1094 // Save the current read position
1095 long pos = ftell(priv->fp);
1096 rewind(priv->fp);
1097
1098 struct FileCompletionData cdata = { false, NULL, NULL, NULL, NULL };
1099 if ((mw_get_field(_("Save to file: "), buf, MUTT_COMP_CLEAR, HC_FILE,
1100 &CompleteFileOps, &cdata) != 0) ||
1101 buf_is_empty(buf))
1102 {
1103 rc = FR_SUCCESS;
1104 goto done;
1105 }
1106
1107 expand_path(buf, false);
1108 fp_save = mutt_file_fopen(buf_string(buf), "a+");
1109 if (!fp_save)
1110 {
1111 mutt_perror("%s", buf_string(buf));
1112 goto done;
1113 }
1114
1115 int bytes = mutt_file_copy_stream(priv->fp, fp_save);
1116 if (bytes == -1)
1117 {
1118 mutt_perror("%s", buf_string(buf));
1119 goto done;
1120 }
1121
1122 mutt_message(_("Saved to: %s"), buf_string(buf));
1123 rc = FR_SUCCESS;
1124
1125done:
1126 // Restore the read position (rewound at start of function)
1127 if (pos >= 0)
1128 (void) mutt_file_seek(priv->fp, pos, SEEK_SET);
1129
1130 mutt_file_fclose(&fp_save);
1131 buf_pool_release(&buf);
1132
1133 return rc;
1134}
1135
1139static int op_search_toggle(struct PagerFunctionData *fdata, const struct KeyEvent *event)
1140{
1141 struct PagerPrivateData *priv = fdata->priv;
1142 if (priv->search_compiled)
1143 {
1144 priv->search_flag ^= MUTT_SEARCH;
1146 }
1147 return FR_SUCCESS;
1148}
1149
1153static int op_view_attachments(struct PagerFunctionData *fdata, const struct KeyEvent *event)
1154{
1155 struct IndexSharedData *shared = fdata->shared;
1156 struct PagerPrivateData *priv = fdata->priv;
1157 struct PagerView *pview = priv->pview;
1158
1159 // This needs to be delegated
1160 if (pview->flags & MUTT_PAGER_ATTACHMENT)
1161 return FR_UNKNOWN;
1162
1163 if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
1164 return FR_NOT_IMPL;
1165 dlg_attach(fdata->n->sub, shared->mailbox_view, shared->email,
1166 pview->pdata->fp, shared->attach_msg);
1167 if (shared->email->attach_del)
1168 shared->mailbox->changed = true;
1170 return FR_SUCCESS;
1171}
1172
1173// -----------------------------------------------------------------------------
1174
1178static const struct PagerFunction PagerFunctions[] = {
1179 // clang-format off
1180 { OP_EXIT, op_exit },
1181 { OP_HALF_DOWN, op_pager_half_down },
1182 { OP_HALF_UP, op_pager_half_up },
1183 { OP_HELP, op_help },
1184 { OP_NEXT_LINE, op_pager_next_line },
1185 { OP_NEXT_PAGE, op_pager_next_page },
1186 { OP_PAGER_BOTTOM, op_pager_bottom },
1187 { OP_PAGER_HIDE_QUOTED, op_pager_hide_quoted },
1188 { OP_PAGER_SKIP_HEADERS, op_pager_skip_headers },
1189 { OP_PAGER_SKIP_QUOTED, op_pager_skip_quoted },
1190 { OP_PAGER_TOP, op_pager_top },
1191 { OP_PREV_LINE, op_pager_prev_line },
1192 { OP_PREV_PAGE, op_pager_prev_page },
1193 { OP_SAVE, op_save },
1194 { OP_SEARCH, op_pager_search },
1195 { OP_SEARCH_REVERSE, op_pager_search },
1196 { OP_SEARCH_NEXT, op_pager_search_next },
1197 { OP_SEARCH_OPPOSITE, op_pager_search_next },
1198 { OP_SEARCH_TOGGLE, op_search_toggle },
1199 { OP_VIEW_ATTACHMENTS, op_view_attachments },
1200 { 0, NULL },
1201 // clang-format on
1202};
1203
1207int pager_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
1208{
1209 if (!win || !event)
1210 {
1213 return FR_ERROR;
1214 }
1215
1216 if (!win->parent || !win->parent->wdata)
1217 {
1219 return FR_ERROR;
1220 }
1221
1222 struct PagerPrivateData *priv = win->parent->wdata;
1223
1224 struct MuttWindow *dlg = dialog_find(win);
1225 if (!dlg || !dlg->wdata)
1226 {
1228 return FR_ERROR;
1229 }
1230
1231 const int op = event->op;
1232
1234
1235 struct PagerFunctionData fdata = {
1236 .n = NeoMutt,
1237 .mod_data = mod_data,
1238 .shared = dlg->wdata,
1239 .priv = priv,
1240 };
1241
1242 int rc = FR_UNKNOWN;
1243 for (size_t i = 0; PagerFunctions[i].op != OP_NULL; i++)
1244 {
1245 const struct PagerFunction *fn = &PagerFunctions[i];
1246 if (fn->op == op)
1247 {
1248 rc = fn->function(&fdata, event);
1249 break;
1250 }
1251 }
1252
1253 if (rc == FR_UNKNOWN) // Not our function
1254 return rc;
1255
1256 const char *result = dispatcher_get_retval_name(rc);
1257 mutt_debug(LL_DEBUG1, "Handled %s (%d) -> %s\n", opcodes_get_name(op), op, NONULL(result));
1258
1260 return rc;
1261}
1262
1268{
1270 ASSERT(mod_data);
1271
1272 return mod_data->menu_pager;
1273}
GUI display the mailboxes in a side panel.
const struct CompleteOps CompleteFileOps
Auto-Completion of Files.
Definition complete.c:152
Select a Mailbox from a list.
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
Color and attribute parsing.
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
struct MuttWindow * dialog_find(struct MuttWindow *win)
Find the parent Dialog of a Window.
Definition dialog.c:89
const char * dispatcher_get_retval_name(int rv)
Get the name of a return value.
Definition dispatcher.c:55
void dispatcher_flush_on_error(int rv)
Flush pending keys after a dispatch error.
Definition dispatcher.c:65
@ FR_SUCCESS
Valid function - successfully performed.
Definition dispatcher.h:40
@ FR_DONE
Exit the Dialog.
Definition dispatcher.h:36
@ FR_UNKNOWN
Unknown function.
Definition dispatcher.h:34
@ FR_ERROR
Valid function - error occurred.
Definition dispatcher.h:39
@ FR_NOT_IMPL
Invalid function - feature not enabled.
Definition dispatcher.h:37
@ FR_NO_ACTION
Valid function - no action performed.
Definition dispatcher.h:38
bool color_is_header(enum ColorId cid)
Colour is for an Email header.
Definition display.c:488
int display_line(FILE *fp, LOFF_T *bytes_read, struct Line **lines, int line_num, int *lines_used, int *lines_max, PagerFlags flags, struct QuoteStyle **quote_list, int *q_level, bool *force_redraw, regex_t *search_re, struct MuttWindow *win_pager, struct AttrColorList *ansi_list)
Print a line on screen.
Definition display.c:1058
Pager Display.
void pager_queue_redraw(struct PagerPrivateData *priv, PagerRedrawFlags redraw)
Queue a request for a redraw.
Definition dlg_pager.c:121
Edit a string.
@ MUTT_COMP_CLEAR
Clear input if printable character is pressed.
Definition wdata.h:47
Structs that make up an email.
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition file.c:224
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:648
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
void mutt_flushinp(void)
MacroEvents moved to KeyModuleData UngetKeyEvents moved to KeyModuleData.
Definition get.c:81
@ MFF_DEPRECATED
Function is deprecated.
Definition get.h:67
static int op_exit(struct AliasFunctionData *fdata, const struct KeyEvent *event)
exit this menu - Implements alias_function_t -
Definition functions.c:312
int pager_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
Perform a Pager function - Implements function_dispatcher_t -.
Definition functions.c:1207
static int op_help(struct EnterFunctionData *fdata, const struct KeyEvent *event)
Display Help - Implements enter_function_t -.
Definition functions.c:476
void dlg_attach(struct ConfigSubset *sub, struct MailboxView *mv, struct Email *e, FILE *fp, bool attach_msg)
Show the attachments in a Menu -.
Definition dlg_attach.c:208
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition window.c:502
static int op_save(struct IndexFunctionData *fdata, const struct KeyEvent *event)
Make decrypted copy - Implements index_function_t -.
Definition functions.c:2936
static int op_view_attachments(struct IndexFunctionData *fdata, const struct KeyEvent *event)
Show MIME attachments - Implements index_function_t -.
Definition functions.c:3265
#define mutt_warning(...)
Definition logging2.h:92
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
static int op_pager_skip_quoted(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Skip beyond quoted text - Implements pager_function_t -.
Definition functions.c:1009
static int op_pager_search_next(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Search for next match - Implements pager_function_t -.
Definition functions.c:774
static int op_pager_half_up(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Scroll up 1/2 page - Implements pager_function_t -.
Definition functions.c:470
static int op_pager_next_page(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Move to the next page - Implements pager_function_t -.
Definition functions.c:534
static int op_pager_prev_page(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Move to the previous page - Implements pager_function_t -.
Definition functions.c:594
static int op_search_toggle(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Toggle search pattern coloring - Implements pager_function_t -.
Definition functions.c:1139
static int op_pager_bottom(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Jump to the bottom of the message - Implements pager_function_t -.
Definition functions.c:416
static int op_pager_hide_quoted(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Toggle display of quoted text - Implements pager_function_t -.
Definition functions.c:491
static int op_pager_next_line(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Scroll down one line - Implements pager_function_t -.
Definition functions.c:513
static int op_pager_prev_line(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Scroll up one line - Implements pager_function_t -.
Definition functions.c:574
static int op_pager_skip_headers(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Jump to first line after headers - Implements pager_function_t -.
Definition functions.c:878
static int op_pager_top(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Jump to the top of the message - Implements pager_function_t -.
Definition functions.c:1030
static int op_pager_half_down(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Scroll down 1/2 page - Implements pager_function_t -.
Definition functions.c:431
static int op_pager_search(struct PagerFunctionData *fdata, const struct KeyEvent *event)
Search for a regular expression - Implements pager_function_t -.
Definition functions.c:622
Convenience wrapper for the gui headers.
void mutt_help(const struct MenuDefinition *md)
Display the Help Page.
Definition help.c:146
Read/write command history from/to a file.
@ HC_FILE
Files.
Definition lib.h:59
@ HC_PATTERN
Patterns.
Definition lib.h:60
bool index_next_undeleted(struct MuttWindow *win_index)
Select the next undeleted Email (if possible)
Definition functions.c:466
GUI manage the main index (list of emails)
void km_menu_add_submenu(struct MenuDefinition *md, struct SubMenu *sm)
Add a SubMenu to a Menu Definition.
Definition init.c:121
struct SubMenu * km_register_submenu(const struct MenuFuncOp functions[])
Register a submenu.
Definition init.c:87
struct MenuDefinition * km_register_menu(int menu, const char *name)
Register a menu.
Definition init.c:104
void km_menu_add_bindings(struct MenuDefinition *md, const struct MenuOpSeq bindings[])
Add Keybindings to a Menu.
Definition init.c:134
Manage keymappings.
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
bool mutt_mb_is_lower(const char *s)
Does a multi-byte string contain only lowercase characters?
Definition mbyte.c:355
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MIN(a, b)
Return the minimum of two values.
Definition memory.h:40
#define MAX(a, b)
Return the maximum of two values.
Definition memory.h:38
GUI present the user with a selectable list.
@ MODULE_ID_PAGER
ModulePager, Pager
Definition module_api.h:83
Convenience wrapper for the library headers.
#define N_(a)
Definition message.h:32
#define _(a)
Definition message.h:28
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition notify.c:173
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:665
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition string.c:586
Many unsorted constants and some structs.
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:122
Some miscellaneous functions.
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:663
@ NT_PAGER
Pager data has changed, NotifyPager, PagerPrivateData.
Definition notify_type.h:54
const char * opcodes_get_name(int op)
Get the name of an opcode.
Definition opcodes.c:48
void pager_init_keys(struct NeoMutt *n, struct SubMenu *sm_generic)
Initialise the Pager Keybindings - Implements ::init_keys_api.
Definition functions.c:302
static int up_n_lines(int nlines, struct Line *info, int cur, bool hiding)
Reposition the pager's view up by n lines.
Definition functions.c:345
static const char * Not_available_in_this_menu
Error message for unavailable functions.
Definition functions.c:58
static int pager_skip_quoted_once(struct PagerPrivateData *priv, struct PagerFunctionData *fdata)
Skip to next unquoted block.
Definition functions.c:921
static const struct MenuOpSeq PagerDefaultBindings[]
Key bindings for the Pager Menu.
Definition functions.c:206
static int down_n_lines(int nlines, struct Line *info, int cur, int max, bool hiding)
Reposition the pager's view down by n lines.
Definition functions.c:366
static const struct MenuFuncOp OpPager[]
Functions for the Pager Menu.
Definition functions.c:66
struct MenuDefinition * pager_get_menu_definition(void)
Get the Pager Menu Definition.
Definition functions.c:1267
bool jump_to_bottom(struct PagerPrivateData *priv, struct PagerView *pview)
Make sure the bottom line is displayed.
Definition functions.c:388
static bool assert_pager_mode(bool test)
Check that pager is in correct mode.
Definition functions.c:327
static const struct PagerFunction PagerFunctions[]
All the NeoMutt functions that the Pager supports.
Definition functions.c:1178
Pager functions.
GUI display a file/email/help in a viewport with paging.
@ NT_PAGER_VIEW
Pager View has changed.
Definition lib.h:192
@ PAGER_LOOP_QUIT
Quit the Pager.
Definition lib.h:154
@ PAGER_REDRAW_PAGER
Redraw the pager.
Definition lib.h:202
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition lib.h:74
#define MUTT_HIDE
Don't show quoted text.
Definition lib.h:66
#define MUTT_TYPES
Compute line's type.
Definition lib.h:68
#define MUTT_SEARCH
Resolve search patterns.
Definition lib.h:67
@ PAGER_MODE_OTHER
Pager is invoked via 3rd path. Non-email content is likely to be shown.
Definition lib.h:143
@ PAGER_MODE_HELP
Pager is invoked via 3rd path to show help.
Definition lib.h:142
@ PAGER_MODE_EMAIL
Pager is invoked via 1st path. The mime part is selected automatically.
Definition lib.h:139
#define MUTT_PAGER_ATTACHMENT
Attachments may exist.
Definition lib.h:73
Pager private Module data.
Private state data for the Pager.
const struct CompleteOps CompletePatternOps
Auto-Completion of Patterns.
Definition complete.c:98
Match patterns to emails.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#define COLOR_QUOTED(cid)
Definition quoted.h:28
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition regex3.h:49
struct SubMenu * sidebar_get_submenu(void)
Get the Sidebar SubMenu.
Definition functions.c:106
GUI display the mailboxes in a side panel.
#define ASSERT(COND)
Definition signal2.h:59
#define NONULL(x)
Definition string2.h:44
String manipulation buffer.
Definition buffer.h:36
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
bool attach_del
Has an attachment marked for deletion.
Definition email.h:99
Input for the file completion function.
Definition curs_lib.h:39
Data shared between Index, Pager and Sidebar.
Definition shared_data.h:37
struct Email * email
Currently selected Email.
Definition shared_data.h:42
struct Mailbox * mailbox
Current Mailbox.
Definition shared_data.h:41
bool attach_msg
Are we in "attach message" mode?
Definition shared_data.h:46
struct MailboxView * mailbox_view
Current Mailbox view.
Definition shared_data.h:40
An event such as a keypress.
Definition get.h:75
int count
Optional count prefix, e.g. 3 for 3j
Definition get.h:78
int op
Function opcode, e.g. OP_HELP.
Definition get.h:77
A line of text in the pager.
Definition display.h:50
short search_arr_size
Number of items in search array.
Definition display.h:59
struct TextSyntax * search
Array of search text in the line.
Definition display.h:60
bool cont_line
Continuation of a previous line (wrapped by NeoMutt)
Definition display.h:53
short cid
Default line colour, e.g. MT_COLOR_SIGNATURE.
Definition display.h:52
LOFF_T offset
Offset into Email file (PagerPrivateData->fp)
Definition display.h:51
bool changed
Mailbox has been modified.
Definition mailbox.h:112
Functions for a Dialog or Window.
Definition menu.h:77
Mapping between a function and an operation.
Definition menu.h:35
Mapping between an operation and a key sequence.
Definition menu.h:45
struct WindowState state
Current state of the Window.
void * wdata
Private data.
struct MuttWindow * parent
Parent Window.
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
FILE * fp
Source stream.
Definition lib.h:164
Data passed to Pager worker functions.
Definition functions.h:37
struct IndexSharedData * shared
Shared Index data.
Definition functions.h:40
struct PagerModuleData * mod_data
Pager module data.
Definition functions.h:39
struct PagerPrivateData * priv
Private Pager data.
Definition functions.h:41
struct NeoMutt * n
NeoMutt application data.
Definition functions.h:38
A NeoMutt function.
Definition functions.h:64
pager_function_t function
Function to call.
Definition functions.h:66
int op
Op code, e.g. OP_MAIN_LIMIT.
Definition functions.h:65
Pager private Module data.
Definition module_data.h:30
struct MenuDefinition * menu_pager
Pager menu definition.
Definition module_data.h:32
Private state data for the Pager.
PagerFlags hide_quoted
Set to MUTT_HIDE when quoted email is hidden <toggle-quoted>
int rc
Return code from functions.
int q_level
Number of unique quoting levels.
int cur_line
Current line (last line visible on screen)
bool wrapped
Has the search/next wrapped around?
int lines_used
Size of lines array (used entries)
char search_str[256]
Current search string.
int lines_max
Capacity of lines array (total entries)
bool force_redraw
Repaint is needed.
enum PagerLoopMode loop
What the Event Loop should do next, e.g. PAGER_LOOP_CONTINUE.
struct Line * lines
Array of text lines in pager.
int has_types
Set to MUTT_TYPES for PAGER_MODE_EMAIL or MUTT_SHOWCOLOR.
struct Notify * notify
Notifications: NotifyPager, PagerPrivateData.
LOFF_T bytes_read
Number of bytes read from file.
int top_line
First visible line on screen.
struct stat st
Stats about Email file.
bool search_back
Search backwards.
struct QuoteStyle * quote_list
Tree of quoting levels.
struct PagerView * pview
Object to view in the pager.
struct AttrColorList ansi_list
List of ANSI colours used in the Pager.
int searchctx
Space to show around search matches.
regex_t search_re
Compiled search string.
int old_top_line
Old top line, used for repainting.
FILE * fp
File containing decrypted/decoded/weeded Email.
PagerFlags search_flag
Set to MUTT_SEARCH when search results are visible <search-toggle>
bool search_compiled
Search regex is in use.
Paged view into some data.
Definition lib.h:173
struct MuttWindow * win_index
Index Window.
Definition lib.h:179
struct PagerData * pdata
Data that pager displays. NOTNULL.
Definition lib.h:174
enum PagerMode mode
Pager mode.
Definition lib.h:175
PagerFlags flags
Additional settings to tweak pager's function.
Definition lib.h:176
struct MuttWindow * win_pager
Pager Window.
Definition lib.h:181
Collection of related functions.
Definition menu.h:65
short rows
Number of rows, can be MUTT_WIN_SIZE_UNLIMITED.
Definition mutt_window.h:61
@ MENU_PAGER
Pager pager (email viewer)
Definition type.h:46