NeoMutt  2021-10-29-220-g2b1eec
Teaching an old dog new tricks
DOXYGEN
dlg_pager.c
Go to the documentation of this file.
1 
34 #include "config.h"
35 #include <assert.h>
36 #include <inttypes.h> // IWYU pragma: keep
37 #include <stdbool.h>
38 #include <stdint.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include "mutt/lib.h"
44 #include "config/lib.h"
45 #include "email/lib.h"
46 #include "core/lib.h"
47 #include "gui/lib.h"
48 #include "mutt.h"
49 #include "lib.h"
50 #include "color/lib.h"
51 #include "index/lib.h"
52 #include "menu/lib.h"
53 #include "context.h"
54 #include "display.h"
55 #include "functions.h"
56 #include "hook.h"
57 #include "keymap.h"
58 #include "mutt_globals.h"
59 #include "mutt_logging.h"
60 #include "mutt_mailbox.h"
61 #include "mx.h"
62 #include "opcodes.h"
63 #include "options.h"
64 #include "private_data.h"
65 #include "protos.h"
66 #include "status.h"
67 
71 struct Resize
72 {
73  int line;
76 };
77 
78 /* hack to return to position when returning from index to same message */
79 static int TopLine = 0;
80 static struct Email *OldEmail = NULL;
81 
82 int braille_row = -1;
83 int braille_col = -1;
84 
85 static struct Resize *Resize = NULL;
86 
88 static const struct Mapping PagerHelp[] = {
89  // clang-format off
90  { N_("Exit"), OP_EXIT },
91  { N_("PrevPg"), OP_PREV_PAGE },
92  { N_("NextPg"), OP_NEXT_PAGE },
93  { N_("Help"), OP_HELP },
94  { NULL, 0 },
95  // clang-format on
96 };
97 
99 static const struct Mapping PagerHelpHelp[] = {
100  // clang-format off
101  { N_("Exit"), OP_EXIT },
102  { N_("PrevPg"), OP_PREV_PAGE },
103  { N_("NextPg"), OP_NEXT_PAGE },
104  { NULL, 0 },
105  // clang-format on
106 };
107 
109 static const struct Mapping PagerNormalHelp[] = {
110  // clang-format off
111  { N_("Exit"), OP_EXIT },
112  { N_("PrevPg"), OP_PREV_PAGE },
113  { N_("NextPg"), OP_NEXT_PAGE },
114  { N_("View Attachm."), OP_VIEW_ATTACHMENTS },
115  { N_("Del"), OP_DELETE },
116  { N_("Reply"), OP_REPLY },
117  { N_("Next"), OP_MAIN_NEXT_UNDELETED },
118  { N_("Help"), OP_HELP },
119  { NULL, 0 },
120  // clang-format on
121 };
122 
123 #ifdef USE_NNTP
125 static const struct Mapping PagerNewsHelp[] = {
126  // clang-format off
127  { N_("Exit"), OP_EXIT },
128  { N_("PrevPg"), OP_PREV_PAGE },
129  { N_("NextPg"), OP_NEXT_PAGE },
130  { N_("Post"), OP_POST },
131  { N_("Followup"), OP_FOLLOWUP },
132  { N_("Del"), OP_DELETE },
133  { N_("Next"), OP_MAIN_NEXT_UNDELETED },
134  { N_("Help"), OP_HELP },
135  { NULL, 0 },
136  // clang-format on
137 };
138 #endif
139 
144 {
145  TopLine = 0;
146  OldEmail = NULL;
147 }
148 
155 {
156  priv->redraw |= redraw;
157  // win_pager->actions |= WA_RECALC;
158 }
159 
164 static void pager_custom_redraw(struct PagerPrivateData *priv)
165 {
166  //---------------------------------------------------------------------------
167  // ASSUMPTIONS & SANITY CHECKS
168  //---------------------------------------------------------------------------
169  // Since pager_custom_redraw() is a static function and it is always called
170  // after mutt_pager() we can rely on a series of sanity checks in
171  // mutt_pager(), namely:
172  // - PAGER_MODE_EMAIL guarantees ( data->email) and (!data->body)
173  // - PAGER_MODE_ATTACH guarantees ( data->email) and ( data->body)
174  // - PAGER_MODE_OTHER guarantees (!data->email) and (!data->body)
175  //
176  // Additionally, while refactoring is still in progress the following checks
177  // are still here to ensure data model consistency.
178  assert(priv); // Redraw function can't be called without its data.
179  assert(priv->pview); // Redraw data can't exist separately without the view.
180  assert(priv->pview->pdata); // View can't exist without its data
181  //---------------------------------------------------------------------------
182 
183  char buf[1024] = { 0 };
184  struct IndexSharedData *shared = dialog_find(priv->pview->win_pager)->wdata;
185 
186  const bool c_tilde = cs_subset_bool(NeoMutt->sub, "tilde");
187  const short c_pager_index_lines =
188  cs_subset_number(NeoMutt->sub, "pager_index_lines");
189 
190  if (priv->redraw & MENU_REDRAW_FULL)
191  {
194 
195  if ((priv->pview->mode == PAGER_MODE_EMAIL) &&
196  ((shared->mailbox->vcount + 1) < c_pager_index_lines))
197  {
198  priv->index_size = shared->mailbox->vcount + 1;
199  }
200  else
201  {
202  priv->index_size = c_pager_index_lines;
203  }
204 
205  priv->indicator = priv->index_size / 3;
206 
207  if (Resize)
208  {
210  if (priv->search_compiled)
211  {
212  uint16_t flags = mutt_mb_is_lower(priv->search_str) ? REG_ICASE : 0;
213  const int err = REG_COMP(&priv->search_re, priv->search_str, REG_NEWLINE | flags);
214  if (err == 0)
215  {
216  priv->search_flag = MUTT_SEARCH;
217  priv->search_back = Resize->search_back;
218  }
219  else
220  {
221  regerror(err, &priv->search_re, buf, sizeof(buf));
222  mutt_error("%s", buf);
223  priv->search_compiled = false;
224  }
225  }
226  priv->win_height = Resize->line;
228 
229  FREE(&Resize);
230  }
231 
232  if ((priv->pview->mode == PAGER_MODE_EMAIL) && (c_pager_index_lines != 0) && priv->menu)
233  {
235 
236  /* some fudge to work out whereabouts the indicator should go */
237  const int index = menu_get_index(priv->menu);
238  if ((index - priv->indicator) < 0)
239  priv->menu->top = 0;
240  else if ((priv->menu->max - index) < (priv->menu->pagelen - priv->indicator))
241  priv->menu->top = priv->menu->max - priv->menu->pagelen;
242  else
243  priv->menu->top = index - priv->indicator;
244 
245  menu_redraw_index(priv->menu);
246  }
247 
249  }
250 
251  // We need to populate more lines, but not change position
252  const bool repopulate = (priv->cur_line > priv->lines_used);
253  if ((priv->redraw & MENU_REDRAW_FLOW) || repopulate)
254  {
255  if (!(priv->pview->flags & MUTT_PAGER_RETWINCH))
256  {
257  priv->win_height = -1;
258  for (int i = 0; i <= priv->top_line; i++)
259  if (!priv->lines[i].cont_line)
260  priv->win_height++;
261  for (int i = 0; i < priv->lines_max; i++)
262  {
263  priv->lines[i].offset = 0;
264  priv->lines[i].cid = -1;
265  priv->lines[i].cont_line = 0;
266  priv->lines[i].syntax_arr_size = 0;
267  priv->lines[i].search_arr_size = -1;
268  priv->lines[i].quote = NULL;
269 
270  mutt_mem_realloc(&(priv->lines[i].syntax), sizeof(struct TextSyntax));
271  if (priv->search_compiled && priv->lines[i].search)
272  FREE(&(priv->lines[i].search));
273  }
274 
275  if (!repopulate)
276  {
277  priv->lines_used = 0;
278  priv->top_line = 0;
279  }
280  }
281  int i = -1;
282  int j = -1;
283  while (display_line(priv->fp, &priv->bytes_read, &priv->lines, ++i,
284  &priv->lines_used, &priv->lines_max,
285  priv->has_types | priv->search_flag | (priv->pview->flags & MUTT_PAGER_NOWRAP),
286  &priv->quote_list, &priv->q_level, &priv->force_redraw,
287  &priv->search_re, priv->pview->win_pager) == 0)
288  {
289  if (!priv->lines[i].cont_line && (++j == priv->win_height))
290  {
291  if (!repopulate)
292  priv->top_line = i;
293  if (!priv->search_flag)
294  break;
295  }
296  }
297  }
298 
299  if ((priv->redraw & MENU_REDRAW_BODY) || (priv->top_line != priv->old_top_line))
300  {
301  do
302  {
303  mutt_window_move(priv->pview->win_pager, 0, 0);
304  priv->cur_line = priv->top_line;
305  priv->old_top_line = priv->top_line;
306  priv->win_height = 0;
307  priv->force_redraw = false;
308 
309  while ((priv->win_height < priv->pview->win_pager->state.rows) &&
310  (priv->lines[priv->cur_line].offset <= priv->st.st_size - 1))
311  {
312  if (display_line(priv->fp, &priv->bytes_read, &priv->lines,
313  priv->cur_line, &priv->lines_used, &priv->lines_max,
314  (priv->pview->flags & MUTT_DISPLAYFLAGS) | priv->hide_quoted |
315  priv->search_flag | (priv->pview->flags & MUTT_PAGER_NOWRAP),
316  &priv->quote_list, &priv->q_level, &priv->force_redraw,
317  &priv->search_re, priv->pview->win_pager) > 0)
318  {
319  priv->win_height++;
320  }
321  priv->cur_line++;
322  mutt_window_move(priv->pview->win_pager, 0, priv->win_height);
323  }
324  } while (priv->force_redraw);
325 
327  while (priv->win_height < priv->pview->win_pager->state.rows)
328  {
330  if (c_tilde)
331  mutt_window_addch(priv->pview->win_pager, '~');
332  priv->win_height++;
333  mutt_window_move(priv->pview->win_pager, 0, priv->win_height);
334  }
336  }
337 
339  mutt_debug(LL_DEBUG5, "repaint done\n");
340 }
341 
348 static const struct Mapping *pager_resolve_help_mapping(enum PagerMode mode, enum MailboxType type)
349 {
350  const struct Mapping *result;
351  switch (mode)
352  {
353  case PAGER_MODE_EMAIL:
354  case PAGER_MODE_ATTACH:
355  case PAGER_MODE_ATTACH_E:
356  if (type == MUTT_NNTP)
357  result = PagerNewsHelp;
358  else
359  result = PagerNormalHelp;
360  break;
361 
362  case PAGER_MODE_HELP:
363  result = PagerHelpHelp;
364  break;
365 
366  case PAGER_MODE_OTHER:
367  result = PagerHelp;
368  break;
369 
370  case PAGER_MODE_UNKNOWN:
371  case PAGER_MODE_MAX:
372  default:
373  assert(false); // something went really wrong
374  }
375  assert(result);
376  return result;
377 }
378 
385 static bool check_read_delay(uint64_t *timestamp)
386 {
387  if ((*timestamp != 0) && (mutt_date_epoch_ms() > *timestamp))
388  {
389  *timestamp = 0;
390  return true;
391  }
392  return false;
393 }
394 
402 static void squash_index_panel(struct MuttWindow *dlg, struct IndexSharedData *shared,
403  struct PagerPrivateData *priv, int pil)
404 {
405  struct MuttWindow *win_pager = priv->pview->win_pager;
406  struct MuttWindow *win_index = priv->pview->win_index;
407 
408  if (win_index)
409  {
410  const int index_space = shared->mailbox ? MIN(pil, shared->mailbox->vcount) : pil;
411  priv->menu = win_index->wdata;
412  win_index->size = MUTT_WIN_SIZE_FIXED;
413  win_index->req_rows = index_space;
414  win_index->parent->size = MUTT_WIN_SIZE_MINIMISE;
415  window_set_visible(win_index->parent, (index_space > 0));
416  }
417 
418  window_set_visible(win_pager->parent, true);
419  mutt_window_reflow(dlg);
420 }
421 
427 static void expand_index_panel(struct MuttWindow *dlg, struct PagerPrivateData *priv)
428 {
429  struct MuttWindow *win_pager = priv->pview->win_pager;
430  struct MuttWindow *win_index = priv->pview->win_index;
431 
432  if (win_index)
433  {
434  win_index->size = MUTT_WIN_SIZE_MAXIMISE;
435  win_index->req_rows = MUTT_WIN_SIZE_UNLIMITED;
436  win_index->parent->size = MUTT_WIN_SIZE_MAXIMISE;
438  window_set_visible(win_index->parent, true);
439  }
440 
441  window_set_visible(win_pager->parent, false);
442  mutt_window_reflow(dlg);
443 }
444 
466 int mutt_pager(struct PagerView *pview)
467 {
468  //===========================================================================
469  // ACT 1 - Ensure sanity of the caller and determine the mode
470  //===========================================================================
471  assert(pview);
472  assert((pview->mode > PAGER_MODE_UNKNOWN) && (pview->mode < PAGER_MODE_MAX));
473  assert(pview->pdata); // view can't exist in a vacuum
474  assert(pview->win_pager);
475  assert(pview->win_pbar);
476 
477  struct MuttWindow *dlg = dialog_find(pview->win_pager);
478  struct IndexSharedData *shared = dlg->wdata;
479 
480  switch (pview->mode)
481  {
482  case PAGER_MODE_EMAIL:
483  // This case was previously identified by IsEmail macro
484  // we expect data to contain email and not contain body
485  // We also expect email to always belong to some mailbox
486  assert(shared->ctx);
487  assert(shared->mailbox);
488  assert(shared->email);
489  assert(!pview->pdata->body);
490  break;
491 
492  case PAGER_MODE_ATTACH:
493  // this case was previously identified by IsAttach and IsMsgAttach
494  // macros, we expect data to contain:
495  // - body (viewing regular attachment)
496  // - fp and body->email in special case of viewing an attached email.
497  assert(pview->pdata->body);
498  if (pview->pdata->fp && pview->pdata->body->email)
499  {
500  // Special case: attachment is a full-blown email message.
501  // Yes, emails can contain other emails.
502  pview->mode = PAGER_MODE_ATTACH_E;
503  }
504  break;
505 
506  case PAGER_MODE_HELP:
507  case PAGER_MODE_OTHER:
508  assert(!shared->ctx);
509  assert(!shared->email);
510  assert(!pview->pdata->body);
511  break;
512 
513  case PAGER_MODE_UNKNOWN:
514  case PAGER_MODE_MAX:
515  default:
516  // Unexpected mode. Catch fire and explode.
517  // This *should* happen if mode is PAGER_MODE_ATTACH_E, since
518  // we do not expect any caller to pass it to us.
519  assert(false);
520  break;
521  }
522 
523  //===========================================================================
524  // ACT 2 - Declare, initialize local variables, read config, etc.
525  //===========================================================================
526 
527  //---------- reading config values needed now--------------------------------
528  const short c_pager_index_lines =
529  cs_subset_number(NeoMutt->sub, "pager_index_lines");
530 
531  //---------- local variables ------------------------------------------------
532  int op = 0;
533  enum MailboxType mailbox_type = shared->mailbox ? shared->mailbox->type : MUTT_UNKNOWN;
534  struct PagerPrivateData *priv = pview->win_pager->parent->wdata;
535  priv->rc = -1;
536  priv->searchctx = 0;
537  priv->first = true;
538  priv->wrapped = false;
539  priv->delay_read_timestamp = 0;
540 
541  {
542  // Wipe any previous state info
543  struct Menu *menu = priv->menu;
544  struct Notify *notify = priv->notify;
545  int rc = priv->rc;
546  memset(priv, 0, sizeof(*priv));
547  priv->rc = rc;
548  priv->menu = menu;
549  priv->notify = notify;
550  priv->win_pbar = pview->win_pbar;
551  }
552 
553  //---------- setup flags ----------------------------------------------------
554  if (!(pview->flags & MUTT_SHOWCOLOR))
555  pview->flags |= MUTT_SHOWFLAT;
556 
557  if ((pview->mode == PAGER_MODE_EMAIL) && !shared->email->read)
558  {
559  shared->ctx->msg_in_pager = shared->email->msgno;
560  priv->win_pbar->actions |= WA_RECALC;
561  const short c_pager_read_delay =
562  cs_subset_number(NeoMutt->sub, "pager_read_delay");
563  if (c_pager_read_delay == 0)
564  {
565  mutt_set_flag(shared->mailbox, shared->email, MUTT_READ, true);
566  }
567  else
568  {
569  priv->delay_read_timestamp = mutt_date_epoch_ms() + (1000 * c_pager_read_delay);
570  }
571  }
572  //---------- setup help menu ------------------------------------------------
573  pview->win_pager->help_data = pager_resolve_help_mapping(pview->mode, mailbox_type);
574  pview->win_pager->help_menu = MENU_PAGER;
575 
576  //---------- initialize redraw pdata -----------------------------------------
578  priv->pview = pview;
579  priv->index_size = c_pager_index_lines;
580  priv->indicator = priv->index_size / 3;
581  priv->lines_max = LINES; // number of lines on screen, from curses
582  priv->lines = mutt_mem_calloc(priv->lines_max, sizeof(struct Line));
583  priv->fp = fopen(pview->pdata->fname, "r");
584  priv->has_types =
585  ((pview->mode == PAGER_MODE_EMAIL) || (pview->flags & MUTT_SHOWCOLOR)) ?
586  MUTT_TYPES :
587  0; // main message or rfc822 attachment
588 
589  for (size_t i = 0; i < priv->lines_max; i++)
590  {
591  priv->lines[i].cid = -1;
592  priv->lines[i].search_arr_size = -1;
593  priv->lines[i].syntax = mutt_mem_malloc(sizeof(struct TextSyntax));
594  (priv->lines[i].syntax)[0].first = -1;
595  (priv->lines[i].syntax)[0].last = -1;
596  }
597 
598  // ---------- try to open the pdata file -------------------------------------
599  if (!priv->fp)
600  {
601  mutt_perror(pview->pdata->fname);
602  return -1;
603  }
604 
605  if (stat(pview->pdata->fname, &priv->st) != 0)
606  {
607  mutt_perror(pview->pdata->fname);
608  mutt_file_fclose(&priv->fp);
609  return -1;
610  }
611  unlink(pview->pdata->fname);
612 
613  //---------- restore global state if needed ---------------------------------
614  while ((pview->mode == PAGER_MODE_EMAIL) && (OldEmail == shared->email) && // are we "resuming" to the same Email?
615  (TopLine != priv->top_line) && // is saved offset different?
616  (priv->lines[priv->cur_line].offset < (priv->st.st_size - 1)))
617  {
619  pager_custom_redraw(priv);
620  // trick user, as if nothing happened
621  // scroll down to previously saved offset
622  priv->top_line = ((TopLine - priv->top_line) > priv->win_height) ?
623  priv->top_line + priv->win_height :
624  TopLine;
625  }
626 
627  TopLine = 0;
628  OldEmail = NULL;
629 
630  //---------- show windows, set focus and visibility --------------------------
631  squash_index_panel(dlg, shared, priv, c_pager_index_lines);
632  window_set_focus(pview->win_pager);
633 
634  //---------- jump to the bottom if requested ------------------------------
635  if (pview->flags & MUTT_PAGER_BOTTOM)
636  {
637  jump_to_bottom(priv, pview);
638  }
639 
640  //-------------------------------------------------------------------------
641  // ACT 3: Read user input and decide what to do with it
642  // ...but also do a whole lot of other things.
643  //-------------------------------------------------------------------------
644  while (op != -1)
645  {
647  {
648  mutt_set_flag(shared->mailbox, shared->email, MUTT_READ, true);
649  }
651 
653  pager_custom_redraw(priv);
654  notify_send(priv->notify, NT_PAGER, NT_PAGER_VIEW, priv);
655  window_redraw(NULL);
656 
657  const bool c_braille_friendly =
658  cs_subset_bool(NeoMutt->sub, "braille_friendly");
659  if (c_braille_friendly)
660  {
661  if (braille_row != -1)
662  {
664  braille_row = -1;
665  }
666  }
667  else
668  mutt_window_move(priv->pview->win_pbar, priv->pview->win_pager->state.cols - 1, 0);
669 
670  // force redraw of the screen at every iteration of the event loop
671  mutt_refresh();
672 
673  //-------------------------------------------------------------------------
674  // Check if information in the status bar needs an update
675  // This is done because pager is a single-threaded application, which
676  // tries to emulate concurrency.
677  //-------------------------------------------------------------------------
678  bool do_new_mail = false;
679  if (shared->mailbox && !OptAttachMsg)
680  {
681  int oldcount = shared->mailbox->msg_count;
682  /* check for new mail */
683  enum MxStatus check = mx_mbox_check(shared->mailbox);
684  if (check == MX_STATUS_ERROR)
685  {
686  if (!shared->mailbox || mutt_buffer_is_empty(&shared->mailbox->pathbuf))
687  {
688  /* fatal error occurred */
690  break;
691  }
692  }
693  else if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED) ||
694  (check == MX_STATUS_FLAGS))
695  {
696  /* notify user of newly arrived mail */
697  if (check == MX_STATUS_NEW_MAIL)
698  {
699  for (size_t i = oldcount; i < shared->mailbox->msg_count; i++)
700  {
701  struct Email *e = shared->mailbox->emails[i];
702 
703  if (e && !e->read)
704  {
705  mutt_message(_("New mail in this mailbox"));
706  do_new_mail = true;
707  break;
708  }
709  }
710  }
711 
712  if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
713  {
714  if (priv->menu && shared->mailbox)
715  {
716  /* After the mailbox has been updated, selection might be invalid */
717  int index = menu_get_index(priv->menu);
718  menu_set_index(priv->menu, MIN(index, MAX(shared->mailbox->msg_count - 1, 0)));
719  index = menu_get_index(priv->menu);
720  struct Email *e = mutt_get_virt_email(shared->mailbox, index);
721  if (!e)
722  continue;
723 
724  bool verbose = shared->mailbox->verbose;
725  shared->mailbox->verbose = false;
726  mutt_update_index(priv->menu, shared->ctx, check, oldcount, shared);
727  shared->mailbox->verbose = verbose;
728 
729  priv->menu->max = shared->mailbox->vcount;
730 
731  /* If these header pointers don't match, then our email may have
732  * been deleted. Make the pointer safe, then leave the pager.
733  * This have a unpleasant behaviour to close the pager even the
734  * deleted message is not the opened one, but at least it's safe. */
735  e = mutt_get_virt_email(shared->mailbox, index);
736  if (shared->email != e)
737  {
738  shared->email = e;
739  break;
740  }
741  }
742 
744  OptSearchInvalid = true;
745  }
746  }
747 
748  if (mutt_mailbox_notify(shared->mailbox) || do_new_mail)
749  {
750  const bool c_beep_new = cs_subset_bool(NeoMutt->sub, "beep_new");
751  if (c_beep_new)
752  mutt_beep(true);
753  const char *const c_new_mail_command =
754  cs_subset_string(NeoMutt->sub, "new_mail_command");
755  if (c_new_mail_command)
756  {
757  char cmd[1024];
758  menu_status_line(cmd, sizeof(cmd), shared, priv->menu, sizeof(cmd),
759  NONULL(c_new_mail_command));
760  if (mutt_system(cmd) != 0)
761  mutt_error(_("Error running \"%s\""), cmd);
762  }
763  }
764  }
765  //-------------------------------------------------------------------------
766 
767  if (SigWinch)
768  {
769  SigWinch = false;
771  clearok(stdscr, true); /* force complete redraw */
773 
775  if (pview->flags & MUTT_PAGER_RETWINCH)
776  {
777  /* Store current position. */
778  priv->win_height = -1;
779  for (size_t i = 0; i <= priv->top_line; i++)
780  if (!priv->lines[i].cont_line)
781  priv->win_height++;
782 
783  Resize = mutt_mem_malloc(sizeof(struct Resize));
784 
785  Resize->line = priv->win_height;
787  Resize->search_back = priv->search_back;
788 
789  op = -1;
790  priv->rc = OP_REFORMAT_WINCH;
791  }
792  else
793  {
794  /* note: mutt_resize_screen() -> mutt_window_reflow() sets
795  * MENU_REDRAW_FULL and MENU_REDRAW_FLOW */
796  op = 0;
797  }
798  continue;
799  }
800  //-------------------------------------------------------------------------
801  // Finally, read user's key press
802  //-------------------------------------------------------------------------
803  // km_dokey() reads not only user's key strokes, but also a MacroBuffer
804  // MacroBuffer may contain OP codes of the operations.
805  // MacroBuffer is global
806  // OP codes inserted into the MacroBuffer by various functions.
807  // One of such functions is `mutt_enter_command()`
808  // Some OP codes are not handled by pager, they cause pager to quit returning
809  // OP code to index. Index handles the operation and then restarts pager
810  op = km_dokey(MENU_PAGER);
811 
812  if (op >= 0)
813  {
815  mutt_debug(LL_DEBUG1, "Got op %s (%d)\n", OpStrings[op][0], op);
816  }
818 
819  if (op < 0)
820  {
821  op = 0;
823  continue;
824  }
825 
826  priv->rc = op;
827 
828  if (op == OP_NULL)
829  {
831  continue;
832  }
833 
834  int rc = pager_function_dispatcher(priv->pview->win_pager, op);
835  if (rc == IR_DONE)
836  break;
837  if (rc == IR_UNKNOWN)
838  break;
839  }
840  //-------------------------------------------------------------------------
841  // END OF ACT 3: Read user input loop - while (op != -1)
842  //-------------------------------------------------------------------------
843 
845  {
846  mutt_set_flag(shared->mailbox, shared->email, MUTT_READ, true);
847  }
848  mutt_file_fclose(&priv->fp);
849  if (pview->mode == PAGER_MODE_EMAIL)
850  {
851  shared->ctx->msg_in_pager = -1;
852  priv->win_pbar->actions |= WA_RECALC;
853  switch (priv->rc)
854  {
855  case -1:
856  case OP_DISPLAY_HEADERS:
858  break;
859  default:
860  TopLine = priv->top_line;
861  OldEmail = shared->email;
862  break;
863  }
864  }
865 
867 
868  for (size_t i = 0; i < priv->lines_max; i++)
869  {
870  FREE(&(priv->lines[i].syntax));
871  if (priv->search_compiled && priv->lines[i].search)
872  FREE(&(priv->lines[i].search));
873  }
874  if (priv->search_compiled)
875  {
876  regfree(&priv->search_re);
877  priv->search_compiled = false;
878  }
879  FREE(&priv->lines);
880 
881  expand_index_panel(dlg, priv);
882 
883  return (priv->rc != -1) ? priv->rc : 0;
884 }
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
Color and attribute parsing.
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:53
@ MT_COLOR_TILDE
Pager: empty lines after message.
Definition: color.h:71
Convenience wrapper for the config headers.
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:412
The "currently-open" mailbox.
Convenience wrapper for the core headers.
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:114
void mutt_beep(bool force)
Irritate the user.
Definition: curs_lib.c:104
uint64_t mutt_date_epoch_ms(void)
Return the number of milliseconds since the Unix epoch.
Definition: date.c:436
struct MuttWindow * dialog_find(struct MuttWindow *win)
Find the parent Dialog of a Window.
Definition: dialog.c:83
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)
Print a line on screen.
Definition: display.c:1039
Pager Display.
void mutt_update_index(struct Menu *menu, struct Context *ctx, enum MxStatus check, int oldcount, struct IndexSharedData *shared)
Update the index.
Definition: dlg_index.c:537
static void expand_index_panel(struct MuttWindow *dlg, struct PagerPrivateData *priv)
Restore the Index Panel.
Definition: dlg_pager.c:427
void mutt_clear_pager_position(void)
Reset the pager's viewing position.
Definition: dlg_pager.c:143
static const struct Mapping * pager_resolve_help_mapping(enum PagerMode mode, enum MailboxType type)
Determine help mapping based on pager mode and mailbox type.
Definition: dlg_pager.c:348
int braille_col
Definition: dlg_pager.c:83
static void pager_custom_redraw(struct PagerPrivateData *priv)
Redraw the pager window.
Definition: dlg_pager.c:164
static struct Email * OldEmail
Definition: dlg_pager.c:80
static void squash_index_panel(struct MuttWindow *dlg, struct IndexSharedData *shared, struct PagerPrivateData *priv, int pil)
Shrink or hide the Index Panel.
Definition: dlg_pager.c:402
static bool check_read_delay(uint64_t *timestamp)
Is it time to mark the message read?
Definition: dlg_pager.c:385
int mutt_pager(struct PagerView *pview)
Display an email, attachment, or help, in a window.
Definition: dlg_pager.c:466
static const struct Mapping PagerHelpHelp[]
Help Bar for the Help Page itself.
Definition: dlg_pager.c:99
static const struct Mapping PagerNewsHelp[]
Help Bar for the Pager of an NNTP Mailbox.
Definition: dlg_pager.c:125
static const struct Mapping PagerNormalHelp[]
Help Bar for the Pager of a normal Mailbox.
Definition: dlg_pager.c:109
void pager_queue_redraw(struct PagerPrivateData *priv, MenuRedrawFlags redraw)
Queue a request for a redraw.
Definition: dlg_pager.c:154
static const struct Mapping PagerHelp[]
Help Bar for the Pager's Help Page.
Definition: dlg_pager.c:88
int braille_row
Definition: dlg_pager.c:82
static int TopLine
Definition: dlg_pager.c:79
void menu_redraw_index(struct Menu *menu)
Force the redraw of the index.
Definition: draw.c:346
Structs that make up an email.
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
#define mutt_perror(...)
Definition: logging.h:88
Convenience wrapper for the gui headers.
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
void mutt_timeout_hook(void)
Execute any timeout hooks.
Definition: hook.c:830
Parse and execute user-defined hooks.
@ IR_DONE
Exit the Index.
Definition: functions.h:37
@ IR_UNKNOWN
Unknown key.
Definition: functions.h:35
GUI manage the main index (list of emails)
int km_dokey(enum MenuType mtype)
Determine what a keypress should do.
Definition: keymap.c:627
void km_error_key(enum MenuType mtype)
Handle an unbound key sequence.
Definition: keymap.c:1132
Manage keymappings.
static const char * timestamp(time_t stamp)
Create a YYYY-MM-DD HH:MM:SS timestamp.
Definition: logging.c:77
@ LL_DEBUG5
Log at debug level 5.
Definition: logging.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
MailboxType
Supported mailbox formats.
Definition: mailbox.h:44
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition: mailbox.h:52
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition: mailbox.h:47
bool mutt_mb_is_lower(const char *s)
Does a multi-byte string contain only lowercase characters?
Definition: mbyte.c:355
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:40
#define MIN(a, b)
Definition: memory.h:31
#define MAX(a, b)
Definition: memory.h:30
GUI present the user with a selectable list.
#define MENU_REDRAW_FULL
Redraw everything.
Definition: lib.h:55
#define MENU_REDRAW_FLOW
Used by pager to reflow text.
Definition: lib.h:57
#define MENU_REDRAW_INDEX
Redraw the index.
Definition: lib.h:52
#define MENU_REDRAW_BODY
Redraw the pager.
Definition: lib.h:56
int menu_get_index(struct Menu *menu)
Get the current selection in the Menu.
Definition: menu.c:624
#define MENU_REDRAW_NO_FLAGS
No flags are set.
Definition: lib.h:51
uint8_t MenuRedrawFlags
Flags, e.g. MENU_REDRAW_INDEX.
Definition: lib.h:48
MenuRedrawFlags menu_set_index(struct Menu *menu, int index)
Set the current selection in the Menu.
Definition: menu.c:638
void msgwin_clear_text(void)
Clear the text in the Message Window.
Definition: msgwin.c:240
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:171
Many unsorted constants and some structs.
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:92
void mutt_curses_set_color_by_id(enum ColorId cid)
Set the current colour for text.
Definition: mutt_curses.c:52
void mutt_curses_set_cursor(enum MuttCursorState state)
Set the cursor state.
Definition: mutt_curses.c:63
void mutt_resize_screen(void)
Update NeoMutt's opinion about the window size (CURSES)
Definition: resize.c:73
@ MUTT_CURSOR_INVISIBLE
Hide the cursor.
Definition: mutt_curses.h:54
@ MUTT_CURSOR_VISIBLE
Display a normal cursor.
Definition: mutt_curses.h:55
Hundreds of global variables to back the user variables.
SIG_ATOMIC_VOLATILE_T SigWinch
true after SIGWINCH is received
Definition: mutt_globals.h:72
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
NeoMutt Logging.
bool mutt_mailbox_notify(struct Mailbox *m_cur)
Notify the user if there's new mail.
Definition: mutt_mailbox.c:236
Mailbox helper functions.
void mutt_window_clear(struct MuttWindow *win)
Clear a Window.
Definition: mutt_window.c:687
void window_redraw(struct MuttWindow *win)
Reflow, recalc and repaint a tree of Windows.
Definition: mutt_window.c:603
void mutt_window_reflow(struct MuttWindow *win)
Resize a Window and its children.
Definition: mutt_window.c:339
int mutt_window_move(struct MuttWindow *win, int col, int row)
Move the cursor in a Window.
Definition: mutt_window.c:292
struct MuttWindow * window_set_focus(struct MuttWindow *win)
Set the Window focus.
Definition: mutt_window.c:651
void window_set_visible(struct MuttWindow *win, bool visible)
Set a Window visible or hidden.
Definition: mutt_window.c:163
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:241
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:380
#define WA_RECALC
Recalculate the contents of the Window.
Definition: mutt_window.h:110
#define MUTT_WIN_SIZE_UNLIMITED
Use as much space as possible.
Definition: mutt_window.h:52
@ MUTT_WIN_SIZE_FIXED
Window has a fixed size.
Definition: mutt_window.h:47
@ MUTT_WIN_SIZE_MINIMISE
Window size depends on its children.
Definition: mutt_window.h:49
@ MUTT_WIN_SIZE_MAXIMISE
Window wants as much space as possible.
Definition: mutt_window.h:48
enum MxStatus mx_mbox_check(struct Mailbox *m)
Check for new mail - Wrapper for MxOps::mbox_check()
Definition: mx.c:1125
API for mailboxes.
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_snc(), and mbox_close()
Definition: mxapi.h:76
@ MX_STATUS_ERROR
An error occurred.
Definition: mxapi.h:77
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition: mxapi.h:82
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mxapi.h:81
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mxapi.h:79
@ NT_PAGER
Pager data has changed, NotifyPager, PagerPrivateData.
Definition: notify_type.h:50
const char * OpStrings[][2]
Definition: opcodes.c:34
All user-callable functions.
Handling of global boolean variables.
bool OptAttachMsg
(pseudo) used by attach-message
Definition: options.h:37
bool OptSearchInvalid
(pseudo) used to invalidate the search pattern
Definition: options.h:57
bool jump_to_bottom(struct PagerPrivateData *priv, struct PagerView *pview)
Make sure the bottom line is displayed.
Definition: functions.c:194
int pager_function_dispatcher(struct MuttWindow *win_pager, int op)
Perform a Pager function.
Definition: functions.c:1967
Pager functions.
#define MUTT_PAGER_RETWINCH
Need reformatting on SIGWINCH.
Definition: lib.h:70
#define NT_PAGER_VIEW
Pager View has changed.
Definition: lib.h:174
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:72
#define MUTT_TYPES
Compute line's type.
Definition: lib.h:64
#define MUTT_DISPLAYFLAGS
Definition: lib.h:77
#define MUTT_SHOWCOLOR
Show characters in color otherwise don't show characters.
Definition: lib.h:61
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:63
#define MUTT_PAGER_BOTTOM
Start at the bottom.
Definition: lib.h:74
PagerMode
Determine the behaviour of the Pager.
Definition: lib.h:133
@ PAGER_MODE_OTHER
Pager is invoked via 3rd path. Non-email content is likely to be shown.
Definition: lib.h:140
@ PAGER_MODE_HELP
Pager is invoked via 3rd path to show help.
Definition: lib.h:139
@ PAGER_MODE_ATTACH
Pager is invoked via 2nd path. A user-selected attachment (mime part or a nested email) will be shown...
Definition: lib.h:137
@ PAGER_MODE_EMAIL
Pager is invoked via 1st path. The mime part is selected automatically.
Definition: lib.h:136
@ PAGER_MODE_ATTACH_E
A special case of PAGER_MODE_ATTACH - attachment is a full-blown email message.
Definition: lib.h:138
@ PAGER_MODE_UNKNOWN
A default and invalid mode, should never be used.
Definition: lib.h:134
@ PAGER_MODE_MAX
Another invalid mode, should never be used.
Definition: lib.h:142
#define MUTT_SHOWFLAT
Show characters (used for displaying help)
Definition: lib.h:60
Private state data for the Pager.
Prototypes for many functions.
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:66
int mutt_system(const char *cmd)
Run an external command.
Definition: system.c:51
void qstyle_free_tree(struct QuoteStyle **quote_list)
Free an entire tree of QuoteStyle.
Definition: quoted.c:149
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:54
void menu_status_line(char *buf, size_t buflen, struct IndexSharedData *shared, struct Menu *menu, int cols, const char *fmt)
Create the status line.
Definition: status.c:445
GUI display a user-configurable status line.
Key value store.
#define NONULL(x)
Definition: string2.h:37
struct Email * email
header information for message/rfc822
Definition: body.h:72
int msg_in_pager
Message currently shown in the pager.
Definition: context.h:43
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
int msgno
Number displayed to the user.
Definition: email.h:111
int index
The absolute (unsorted) message number.
Definition: email.h:110
Data shared between Index, Pager and Sidebar.
Definition: shared_data.h:37
struct Context * ctx
Current Mailbox view.
Definition: shared_data.h:39
struct Email * email
Currently selected Email.
Definition: shared_data.h:42
struct Mailbox * mailbox
Current Mailbox.
Definition: shared_data.h:41
A line of text in the pager.
Definition: display.h:71
short search_arr_size
Number of items in search array.
Definition: display.h:80
struct TextSyntax * search
Array of search text in the line.
Definition: display.h:81
bool cont_line
Continuation of a previous line (wrapped by NeoMutt)
Definition: display.h:74
short cid
Default line colour, e.g. MT_COLOR_QUOTED.
Definition: display.h:73
struct QuoteStyle * quote
Quoting style for this line (pointer into PagerPrivateData->quote_list)
Definition: display.h:83
LOFF_T offset
Offset into Email file (PagerPrivateData->fp)
Definition: display.h:72
short syntax_arr_size
Number of items in syntax array.
Definition: display.h:77
struct TextSyntax * syntax
Array of coloured text in the line.
Definition: display.h:78
int vcount
The number of virtual messages.
Definition: mailbox.h:102
int msg_count
Total number of messages.
Definition: mailbox.h:91
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
struct Buffer pathbuf
Path of the Mailbox.
Definition: mailbox.h:83
bool verbose
Display status messages?
Definition: mailbox.h:118
Mapping between user-readable string and a constant.
Definition: mapping.h:32
Definition: lib.h:69
int top
Entry that is the top of the current page.
Definition: lib.h:88
int pagelen
Number of entries per screen.
Definition: lib.h:74
int max
Number of entries in the menu.
Definition: lib.h:71
const struct Mapping * help_data
Data for the Help Bar.
Definition: mutt_window.h:142
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
void * wdata
Private data.
Definition: mutt_window.h:145
short req_rows
Number of rows required.
Definition: mutt_window.h:125
int help_menu
Menu for key bindings, e.g. MENU_PAGER.
Definition: mutt_window.h:141
struct MuttWindow * parent
Parent Window.
Definition: mutt_window.h:135
WindowActionFlags actions
Actions to be performed, e.g. WA_RECALC.
Definition: mutt_window.h:132
enum MuttWindowSize size
Type of Window, e.g. MUTT_WIN_SIZE_FIXED.
Definition: mutt_window.h:131
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Notification API.
Definition: notify.c:51
const char * fname
Name of the file to read.
Definition: lib.h:153
FILE * fp
Source stream.
Definition: lib.h:151
struct Body * body
Current attachment.
Definition: lib.h:150
Private state data for the Pager.
Definition: private_data.h:41
PagerFlags hide_quoted
Set to MUTT_HIDE when quoted email is hidden <toggle-quoted>
Definition: private_data.h:62
int rc
Return code from functions.
Definition: private_data.h:77
int q_level
Number of unique quoting levels.
Definition: private_data.h:61
int cur_line
Current line (last line visible on screen)
Definition: private_data.h:53
bool wrapped
Has the search/next wrapped around?
Definition: private_data.h:80
int lines_used
Size of lines array (used entries)
Definition: private_data.h:51
char search_str[256]
Current search string.
Definition: private_data.h:65
int lines_max
Capacity of lines array (total entries)
Definition: private_data.h:52
int index_size
Size of the mini-index Window $pager_index_lines
Definition: private_data.h:70
uint64_t delay_read_timestamp
Time that email was first shown.
Definition: private_data.h:81
struct MuttWindow * win_pbar
Pager Bar.
Definition: private_data.h:43
bool force_redraw
Repaint is needed.
Definition: private_data.h:73
struct Line * lines
Array of text lines in pager.
Definition: private_data.h:50
int has_types
Set to MUTT_TYPES for PAGER_MODE_EMAIL or MUTT_SHOWCOLOR.
Definition: private_data.h:58
struct Notify * notify
Notifications: NotifyPager, PagerPrivateData.
Definition: private_data.h:75
struct Menu * menu
Pager Menu.
Definition: private_data.h:42
LOFF_T bytes_read
Number of bytes read from file.
Definition: private_data.h:48
int top_line
First visible line on screen.
Definition: private_data.h:57
struct stat st
Stats about Email file.
Definition: private_data.h:47
bool first
First time flag for toggle-new.
Definition: private_data.h:79
bool search_back
Search backwards.
Definition: private_data.h:68
struct QuoteStyle * quote_list
Tree of quoting levels.
Definition: private_data.h:60
struct PagerView * pview
Object to view in the pager.
Definition: private_data.h:44
int searchctx
Space to show around search matches.
Definition: private_data.h:78
int indicator
Preferred position of the indicator line in the mini-index Window.
Definition: private_data.h:71
MenuRedrawFlags redraw
When to redraw the screen.
Definition: private_data.h:74
regex_t search_re
Compiled search string.
Definition: private_data.h:67
int win_height
Number of lines in the Window.
Definition: private_data.h:56
int old_top_line
Old top line, used for repainting.
Definition: private_data.h:55
FILE * fp
File containing decrypted/decoded/weeded Email.
Definition: private_data.h:46
PagerFlags search_flag
Set to MUTT_SEARCH when search results are visible <search-toggle>
Definition: private_data.h:64
bool search_compiled
Search regex is in use.
Definition: private_data.h:66
Paged view into some data.
Definition: lib.h:160
struct MuttWindow * win_index
Index Window.
Definition: lib.h:166
struct PagerData * pdata
Data that pager displays. NOTNULL.
Definition: lib.h:161
enum PagerMode mode
Pager mode.
Definition: lib.h:162
PagerFlags flags
Additional settings to tweak pager's function.
Definition: lib.h:163
struct MuttWindow * win_pbar
Pager Bar Window.
Definition: lib.h:167
struct MuttWindow * win_pager
Pager Window.
Definition: lib.h:168
Keep track of screen resizing.
Definition: dlg_pager.c:72
bool search_compiled
Definition: dlg_pager.c:74
bool search_back
Definition: dlg_pager.c:75
int line
Definition: dlg_pager.c:73
Highlighting for a piece of text.
Definition: display.h:50
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60
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:54