NeoMutt  2021-02-05-89-gabe350
Teaching an old dog new tricks
DOXYGEN
index.c
Go to the documentation of this file.
1 
30 #include "config.h"
31 #include <assert.h>
32 #include <ctype.h>
33 #include <limits.h>
34 #include <stdbool.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include "private.h"
40 #include "mutt/lib.h"
41 #include "config/lib.h"
42 #include "email/lib.h"
43 #include "core/lib.h"
44 #include "alias/lib.h"
45 #include "conn/lib.h"
46 #include "gui/lib.h"
47 #include "mutt.h"
48 #include "debug/lib.h"
49 #include "lib.h"
50 #include "ncrypt/lib.h"
51 #include "pager/lib.h"
52 #include "pattern/lib.h"
53 #include "send/lib.h"
54 #include "browser.h"
55 #include "commands.h"
56 #include "context.h"
57 #include "format_flags.h"
58 #include "hdrline.h"
59 #include "hook.h"
60 #include "keymap.h"
61 #include "mutt_globals.h"
62 #include "mutt_header.h"
63 #include "mutt_logging.h"
64 #include "mutt_mailbox.h"
65 #include "mutt_menu.h"
66 #include "mutt_thread.h"
67 #include "muttlib.h"
68 #include "mx.h"
69 #include "opcodes.h"
70 #include "options.h"
71 #include "progress.h"
72 #include "protos.h"
73 #include "recvattach.h"
74 #include "score.h"
75 #include "sort.h"
76 #include "status.h"
77 #ifdef USE_SIDEBAR
78 #include "sidebar/lib.h"
79 #endif
80 #ifdef USE_POP
81 #include "pop/lib.h"
82 #endif
83 #ifdef USE_IMAP
84 #include "imap/lib.h"
85 #endif
86 #ifdef USE_NOTMUCH
87 #include "notmuch/lib.h"
88 #endif
89 #ifdef USE_NNTP
90 #include "nntp/lib.h"
91 #include "nntp/adata.h"
92 #include "nntp/mdata.h"
93 #endif
94 #ifdef ENABLE_NLS
95 #include <libintl.h>
96 #endif
97 #ifdef USE_INOTIFY
98 #include "monitor.h"
99 #endif
100 #ifdef USE_AUTOCRYPT
101 #include "autocrypt/lib.h"
102 #endif
103 
105 static const struct Mapping IndexHelp[] = {
106  // clang-format off
107  { N_("Quit"), OP_QUIT },
108  { N_("Del"), OP_DELETE },
109  { N_("Undel"), OP_UNDELETE },
110  { N_("Save"), OP_SAVE },
111  { N_("Mail"), OP_MAIL },
112  { N_("Reply"), OP_REPLY },
113  { N_("Group"), OP_GROUP_REPLY },
114  { N_("Help"), OP_HELP },
115  { NULL, 0 },
116  // clang-format on
117 };
118 
119 #ifdef USE_NNTP
120 static const struct Mapping IndexNewsHelp[] = {
122  // clang-format off
123  { N_("Quit"), OP_QUIT },
124  { N_("Del"), OP_DELETE },
125  { N_("Undel"), OP_UNDELETE },
126  { N_("Save"), OP_SAVE },
127  { N_("Post"), OP_POST },
128  { N_("Followup"), OP_FOLLOWUP },
129  { N_("Catchup"), OP_CATCHUP },
130  { N_("Help"), OP_HELP },
131  { NULL, 0 },
132  // clang-format on
133 };
134 #endif
135 
136 // clang-format off
140 typedef uint8_t CheckFlags;
141 #define CHECK_NO_FLAGS 0
142 #define CHECK_IN_MAILBOX (1 << 0)
143 #define CHECK_MSGCOUNT (1 << 1)
144 #define CHECK_VISIBLE (1 << 2)
145 #define CHECK_READONLY (1 << 3)
146 #define CHECK_ATTACH (1 << 4)
147 // clang-format on
148 
156 static bool prereq(struct Context *ctx, struct Menu *menu, CheckFlags checks)
157 {
158  bool result = true;
159 
160  if (checks & (CHECK_MSGCOUNT | CHECK_VISIBLE | CHECK_READONLY))
161  checks |= CHECK_IN_MAILBOX;
162 
163  if ((checks & CHECK_IN_MAILBOX) && (!ctx || !ctx->mailbox))
164  {
165  mutt_error(_("No mailbox is open"));
166  result = false;
167  }
168 
169  if (result && (checks & CHECK_MSGCOUNT) && (ctx->mailbox->msg_count == 0))
170  {
171  mutt_error(_("There are no messages"));
172  result = false;
173  }
174 
175  if (result && (checks & CHECK_VISIBLE) && (menu->current >= ctx->mailbox->vcount))
176  {
177  mutt_error(_("No visible messages"));
178  result = false;
179  }
180 
181  if (result && (checks & CHECK_READONLY) && ctx->mailbox->readonly)
182  {
183  mutt_error(_("Mailbox is read-only"));
184  result = false;
185  }
186 
187  if (result && (checks & CHECK_ATTACH) && OptAttachMsg)
188  {
189  mutt_error(_("Function not permitted in attach-message mode"));
190  result = false;
191  }
192 
193  if (!result)
194  mutt_flushinp();
195 
196  return result;
197 }
198 
206 static bool check_acl(struct Mailbox *m, AclFlags acl, const char *msg)
207 {
208  if (!m)
209  return false;
210 
211  if (!(m->rights & acl))
212  {
213  /* L10N: %s is one of the CHECK_ACL entries below. */
214  mutt_error(_("%s: Operation not permitted by ACL"), msg);
215  return false;
216  }
217 
218  return true;
219 }
220 
233 static void collapse_all(struct Context *ctx, struct Menu *menu, int toggle)
234 {
235  if (!ctx || !ctx->mailbox || (ctx->mailbox->msg_count == 0) || !menu)
236  return;
237 
238  struct Email *e_cur = mutt_get_virt_email(ctx->mailbox, menu->current);
239  if (!e_cur)
240  return;
241 
242  int final;
243 
244  /* Figure out what the current message would be after folding / unfolding,
245  * so that we can restore the cursor in a sane way afterwards. */
246  if (e_cur->collapsed && toggle)
247  final = mutt_uncollapse_thread(e_cur);
248  else if (mutt_thread_can_collapse(e_cur))
249  final = mutt_collapse_thread(e_cur);
250  else
251  final = e_cur->vnum;
252 
253  if (final == -1)
254  return;
255 
256  struct Email *base = mutt_get_virt_email(ctx->mailbox, final);
257  if (!base)
258  return;
259 
260  /* Iterate all threads, perform collapse/uncollapse as needed */
261  ctx->collapsed = toggle ? !ctx->collapsed : true;
263 
264  /* Restore the cursor */
265  mutt_set_vnum(ctx->mailbox);
266  for (int i = 0; i < ctx->mailbox->vcount; i++)
267  {
268  struct Email *e = mutt_get_virt_email(ctx->mailbox, i);
269  if (!e)
270  break;
271  if (e->index == base->index)
272  {
273  menu->current = i;
274  break;
275  }
276  }
277 
279 }
280 
288 static int ci_next_undeleted(struct Mailbox *m, int msgno)
289 {
290  if (!m)
291  return -1;
292 
293  for (int i = msgno + 1; i < m->vcount; i++)
294  {
295  struct Email *e = mutt_get_virt_email(m, i);
296  if (!e)
297  continue;
298  if (!e->deleted)
299  return i;
300  }
301  return -1;
302 }
303 
311 static int ci_previous_undeleted(struct Mailbox *m, int msgno)
312 {
313  if (!m)
314  return -1;
315 
316  for (int i = msgno - 1; i >= 0; i--)
317  {
318  struct Email *e = mutt_get_virt_email(m, i);
319  if (!e)
320  continue;
321  if (!e->deleted)
322  return i;
323  }
324  return -1;
325 }
326 
335 static int ci_first_message(struct Mailbox *m)
336 {
337  if (!m || (m->msg_count == 0))
338  return 0;
339 
340  int old = -1;
341  for (int i = 0; i < m->vcount; i++)
342  {
343  struct Email *e = mutt_get_virt_email(m, i);
344  if (!e)
345  continue;
346  if (!e->read && !e->deleted)
347  {
348  if (!e->old)
349  return i;
350  if (old == -1)
351  old = i;
352  }
353  }
354  if (old != -1)
355  return old;
356 
357  /* If `$sort` is reverse and not threaded, the latest message is first.
358  * If `$sort` is threaded, the latest message is first if exactly one
359  * of `$sort` and `$sort_aux` are reverse. */
360  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
361  const short c_sort_aux = cs_subset_sort(NeoMutt->sub, "sort_aux");
362  if (((c_sort & SORT_REVERSE) && ((c_sort & SORT_MASK) != SORT_THREADS)) ||
363  (((c_sort & SORT_MASK) == SORT_THREADS) && ((c_sort ^ c_sort_aux) & SORT_REVERSE)))
364  {
365  return 0;
366  }
367  else
368  {
369  return m->vcount ? m->vcount - 1 : 0;
370  }
371 
372  return 0;
373 }
374 
383 static int mx_toggle_write(struct Mailbox *m)
384 {
385  if (!m)
386  return -1;
387 
388  if (m->readonly)
389  {
390  mutt_error(_("Can't toggle write on a readonly mailbox"));
391  return -1;
392  }
393 
394  if (m->dontwrite)
395  {
396  m->dontwrite = false;
397  mutt_message(_("Changes to folder will be written on folder exit"));
398  }
399  else
400  {
401  m->dontwrite = true;
402  mutt_message(_("Changes to folder will not be written"));
403  }
404 
405  return 0;
406 }
407 
413 static void resort_index(struct Context *ctx, struct Menu *menu)
414 {
415  if (!ctx || !ctx->mailbox || !menu)
416  return;
417 
418  struct Email *e_cur = mutt_get_virt_email(ctx->mailbox, menu->current);
419 
420  menu->current = -1;
421  mutt_sort_headers(ctx->mailbox, ctx->threads, false, &ctx->vsize);
422  /* Restore the current message */
423 
424  for (int i = 0; i < ctx->mailbox->vcount; i++)
425  {
426  struct Email *e = mutt_get_virt_email(ctx->mailbox, i);
427  if (!e)
428  continue;
429  if (e == e_cur)
430  {
431  menu->current = i;
432  break;
433  }
434  }
435 
436  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
437  if (((c_sort & SORT_MASK) == SORT_THREADS) && (menu->current < 0))
438  menu->current = mutt_parent_message(e_cur, false);
439 
440  if (menu->current < 0)
441  menu->current = ci_first_message(ctx->mailbox);
442 
443  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
444 }
445 
452 static void update_index_threaded(struct Context *ctx, enum MxStatus check, int oldcount)
453 {
454  struct Email **save_new = NULL;
455  const bool lmt = ctx_has_limit(ctx);
456 
457  int num_new = MAX(0, ctx->mailbox->msg_count - oldcount);
458 
459  const bool c_uncollapse_new = cs_subset_bool(NeoMutt->sub, "uncollapse_new");
460  /* save the list of new messages */
461  if ((check != MX_STATUS_REOPENED) && (oldcount > 0) &&
462  (lmt || c_uncollapse_new) && (num_new > 0))
463  {
464  save_new = mutt_mem_malloc(num_new * sizeof(struct Email *));
465  for (int i = oldcount; i < ctx->mailbox->msg_count; i++)
466  save_new[i - oldcount] = ctx->mailbox->emails[i];
467  }
468 
469  /* Sort first to thread the new messages, because some patterns
470  * require the threading information.
471  *
472  * If the mailbox was reopened, need to rethread from scratch. */
473  mutt_sort_headers(ctx->mailbox, ctx->threads, (check == MX_STATUS_REOPENED), &ctx->vsize);
474 
475  if (lmt)
476  {
477  /* Because threading changes the order in ctx->mailbox->emails, we don't
478  * know which emails are new. Hence, we need to re-apply the limit to the
479  * whole set.
480  */
481  for (int i = 0; i < ctx->mailbox->msg_count; i++)
482  {
483  struct Email *e = ctx->mailbox->emails[i];
485  ctx->mailbox, e, NULL))
486  {
487  /* vnum will get properly set by mutt_set_vnum(), which
488  * is called by mutt_sort_headers() just below. */
489  e->vnum = 1;
490  e->visible = true;
491  }
492  else
493  {
494  e->vnum = -1;
495  e->visible = false;
496  }
497  }
498  /* Need a second sort to set virtual numbers and redraw the tree */
499  mutt_sort_headers(ctx->mailbox, ctx->threads, false, &ctx->vsize);
500  }
501 
502  /* uncollapse threads with new mail */
503  if (c_uncollapse_new)
504  {
505  if (check == MX_STATUS_REOPENED)
506  {
507  ctx->collapsed = false;
509  mutt_set_vnum(ctx->mailbox);
510  }
511  else if (oldcount > 0)
512  {
513  for (int j = 0; j < num_new; j++)
514  {
515  if (save_new[j]->visible)
516  {
517  mutt_uncollapse_thread(save_new[j]);
518  }
519  }
520  mutt_set_vnum(ctx->mailbox);
521  }
522  }
523 
524  FREE(&save_new);
525 }
526 
532 static void update_index_unthreaded(struct Context *ctx, enum MxStatus check)
533 {
534  /* We are in a limited view. Check if the new message(s) satisfy
535  * the limit criteria. If they do, set their virtual msgno so that
536  * they will be visible in the limited view */
537  if (ctx_has_limit(ctx))
538  {
539  int padding = mx_msg_padding_size(ctx->mailbox);
540  ctx->mailbox->vcount = ctx->vsize = 0;
541  for (int i = 0; i < ctx->mailbox->msg_count; i++)
542  {
543  struct Email *e = ctx->mailbox->emails[i];
544  if (!e)
545  break;
547  MUTT_MATCH_FULL_ADDRESS, ctx->mailbox, e, NULL))
548  {
549  assert(ctx->mailbox->vcount < ctx->mailbox->msg_count);
550  e->vnum = ctx->mailbox->vcount;
551  ctx->mailbox->v2r[ctx->mailbox->vcount] = i;
552  e->visible = true;
553  ctx->mailbox->vcount++;
554  struct Body *b = e->body;
555  ctx->vsize += b->length + b->offset - b->hdr_offset + padding;
556  }
557  else
558  {
559  e->visible = false;
560  }
561  }
562  }
563 
564  /* if the mailbox was reopened, need to rethread from scratch */
565  mutt_sort_headers(ctx->mailbox, ctx->threads, (check == MX_STATUS_REOPENED), &ctx->vsize);
566 }
567 
572 {
573  struct Email *e;
574  size_t sequence;
575 };
576 
584 static bool is_current_email(const struct CurrentEmail *cur, const struct Email *e)
585 {
586  return cur->sequence == e->sequence;
587 }
588 
594 static void set_current_email(struct CurrentEmail *cur, struct Email *e)
595 {
596  cur->e = e;
597  cur->sequence = e ? e->sequence : 0;
598 }
599 
608 static void update_index(struct Menu *menu, struct Context *ctx, enum MxStatus check,
609  int oldcount, const struct CurrentEmail *cur)
610 {
611  if (!menu || !ctx)
612  return;
613 
614  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
615  if ((c_sort & SORT_MASK) == SORT_THREADS)
616  update_index_threaded(ctx, check, oldcount);
617  else
618  update_index_unthreaded(ctx, check);
619 
620  const int old_current = menu->current;
621  menu->current = -1;
622  if (oldcount)
623  {
624  /* restore the current message to the message it was pointing to */
625  for (int i = 0; i < ctx->mailbox->vcount; i++)
626  {
627  struct Email *e = mutt_get_virt_email(ctx->mailbox, i);
628  if (!e)
629  continue;
630  if (is_current_email(cur, e))
631  {
632  menu->current = i;
633  break;
634  }
635  }
636  }
637 
638  if (menu->current < 0)
639  menu->current = (old_current < ctx->mailbox->vcount) ?
640  old_current :
642 }
643 
654 void mutt_update_index(struct Menu *menu, struct Context *ctx, enum MxStatus check,
655  int oldcount, const struct Email *cur_email)
656 {
657  struct CurrentEmail se = { .e = NULL, .sequence = cur_email->sequence };
658  update_index(menu, ctx, check, oldcount, &se);
659 }
660 
667 {
668  if (!nc->global_data)
669  return -1;
670  if ((nc->event_type != NT_MAILBOX) || (nc->event_subtype != NT_MAILBOX_CLOSED))
671  return 0;
672 
673  struct Mailbox **ptr = nc->global_data;
674  if (!ptr || !*ptr)
675  return 0;
676 
677  *ptr = NULL;
678  return 0;
679 }
680 
689 static void change_folder_mailbox(struct Menu *menu, struct Mailbox *m, int *oldcount,
690  const struct CurrentEmail *cur, bool read_only)
691 {
692  if (!m)
693  return;
694 
695  struct Mailbox *m_ctx = ctx_mailbox(Context);
696  /* keepalive failure in mutt_enter_fname may kill connection. */
697  if (m_ctx && (mutt_buffer_is_empty(&m_ctx->pathbuf)))
698  ctx_free(&Context);
699 
700  if (m_ctx)
701  {
702  char *new_last_folder = NULL;
703 #ifdef USE_INOTIFY
704  int monitor_remove_rc = mutt_monitor_remove(NULL);
705 #endif
706 #ifdef USE_COMP_MBOX
707  if (m_ctx->compress_info && (m_ctx->realpath[0] != '\0'))
708  new_last_folder = mutt_str_dup(m_ctx->realpath);
709  else
710 #endif
711  new_last_folder = mutt_str_dup(mailbox_path(m_ctx));
712  *oldcount = m_ctx->msg_count;
713 
714  const enum MxStatus check = mx_mbox_close(&Context);
715  if (check != MX_STATUS_OK)
716  {
717 #ifdef USE_INOTIFY
718  if (monitor_remove_rc == 0)
719  mutt_monitor_add(NULL);
720 #endif
721  if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
722  update_index(menu, Context, check, *oldcount, cur);
723 
724  FREE(&new_last_folder);
725  OptSearchInvalid = true;
726  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
727  return;
728  }
729  FREE(&LastFolder);
730  LastFolder = new_last_folder;
731  }
733 
734  /* If the `folder-hook` were to call `unmailboxes`, then the Mailbox (`m`)
735  * could be deleted, leaving `m` dangling. */
736  // TODO: Refactor this function to avoid the need for an observer
738  char *dup_path = mutt_str_dup(mailbox_path(m));
739  char *dup_name = mutt_str_dup(m->name);
740 
741  mutt_folder_hook(dup_path, dup_name);
742  if (m)
743  {
744  /* `m` is still valid, but we won't need the observer again before the end
745  * of the function. */
747  }
748 
749  // Recreate the Mailbox as the folder-hook might have invoked `mailboxes`
750  // and/or `unmailboxes`.
751  m = mx_path_resolve(dup_path);
752  FREE(&dup_path);
753  FREE(&dup_name);
754 
755  if (!m)
756  return;
757 
759  Context = mx_mbox_open(m, flags);
760  if (Context)
761  {
763 #ifdef USE_INOTIFY
764  mutt_monitor_add(NULL);
765 #endif
766  }
767  else
768  {
769  menu->current = 0;
770  }
771 
772  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
773  const bool c_collapse_all = cs_subset_bool(NeoMutt->sub, "collapse_all");
774  if (((c_sort & SORT_MASK) == SORT_THREADS) && c_collapse_all)
775  collapse_all(Context, menu, 0);
776 
777  struct MuttWindow *dlg = dialog_find(menu->win_index);
778  struct EventMailbox em = { Context ? Context->mailbox : NULL };
780 
782  /* force the mailbox check after we have changed the folder */
784  menu->redraw = REDRAW_FULL;
785  OptSearchInvalid = true;
786 }
787 
788 #ifdef USE_NOTMUCH
789 
798 static struct Mailbox *change_folder_notmuch(struct Menu *menu, char *buf,
799  int buflen, int *oldcount,
800  const struct CurrentEmail *cur, bool read_only)
801 {
802  if (!nm_url_from_query(NULL, buf, buflen))
803  {
804  mutt_message(_("Failed to create query, aborting"));
805  return NULL;
806  }
807 
808  struct Mailbox *m_query = mx_path_resolve(buf);
809  change_folder_mailbox(menu, m_query, oldcount, cur, read_only);
810  return m_query;
811 }
812 #endif
813 
824 static void change_folder_string(struct Menu *menu, char *buf, size_t buflen,
825  int *oldcount, const struct CurrentEmail *cur,
826  bool *pager_return, bool read_only)
827 {
828 #ifdef USE_NNTP
829  if (OptNews)
830  {
831  OptNews = false;
832  nntp_expand_path(buf, buflen, &CurrentNewsSrv->conn->account);
833  }
834  else
835 #endif
836  {
837  const char *c_folder = cs_subset_string(NeoMutt->sub, "folder");
838  mx_path_canon(buf, buflen, c_folder, NULL);
839  }
840 
841  enum MailboxType type = mx_path_probe(buf);
842  if ((type == MUTT_MAILBOX_ERROR) || (type == MUTT_UNKNOWN))
843  {
844  // Look for a Mailbox by its description, before failing
845  struct Mailbox *m = mailbox_find_name(buf);
846  if (m)
847  {
848  change_folder_mailbox(menu, m, oldcount, cur, read_only);
849  *pager_return = false;
850  }
851  else
852  mutt_error(_("%s is not a mailbox"), buf);
853  return;
854  }
855 
856  /* past this point, we don't return to the pager on error */
857  *pager_return = false;
858 
859  struct Mailbox *m = mx_path_resolve(buf);
860  change_folder_mailbox(menu, m, oldcount, cur, read_only);
861 }
862 
866 void index_make_entry(struct Menu *menu, char *buf, size_t buflen, int line)
867 {
868  buf[0] = '\0';
869 
870  struct Mailbox *m = ctx_mailbox(Context);
871 
872  if (!m || !menu || (line < 0) || (line >= m->email_max))
873  return;
874 
875  struct Email *e = mutt_get_virt_email(m, line);
876  if (!e)
877  return;
878 
880  struct MuttThread *tmp = NULL;
881 
882  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
883  if (((c_sort & SORT_MASK) == SORT_THREADS) && e->tree)
884  {
885  flags |= MUTT_FORMAT_TREE; /* display the thread tree */
886  if (e->display_subject)
887  flags |= MUTT_FORMAT_FORCESUBJ;
888  else
889  {
890  const int reverse = c_sort & SORT_REVERSE;
891  int edgemsgno;
892  if (reverse)
893  {
894  if (menu->top + menu->pagelen > menu->max)
895  edgemsgno = m->v2r[menu->max - 1];
896  else
897  edgemsgno = m->v2r[menu->top + menu->pagelen - 1];
898  }
899  else
900  edgemsgno = m->v2r[menu->top];
901 
902  for (tmp = e->thread->parent; tmp; tmp = tmp->parent)
903  {
904  if (!tmp->message)
905  continue;
906 
907  /* if no ancestor is visible on current screen, provisionally force
908  * subject... */
909  if (reverse ? (tmp->message->msgno > edgemsgno) : (tmp->message->msgno < edgemsgno))
910  {
911  flags |= MUTT_FORMAT_FORCESUBJ;
912  break;
913  }
914  else if (tmp->message->vnum >= 0)
915  break;
916  }
917  if (flags & MUTT_FORMAT_FORCESUBJ)
918  {
919  for (tmp = e->thread->prev; tmp; tmp = tmp->prev)
920  {
921  if (!tmp->message)
922  continue;
923 
924  /* ...but if a previous sibling is available, don't force it */
925  if (reverse ? (tmp->message->msgno > edgemsgno) : (tmp->message->msgno < edgemsgno))
926  break;
927  else if (tmp->message->vnum >= 0)
928  {
929  flags &= ~MUTT_FORMAT_FORCESUBJ;
930  break;
931  }
932  }
933  }
934  }
935  }
936 
937  const char *c_index_format = cs_subset_string(NeoMutt->sub, "index_format");
938  mutt_make_string(buf, buflen, menu->win_index->state.cols, NONULL(c_index_format),
939  m, Context->msg_in_pager, e, flags, NULL);
940 }
941 
945 int index_color(struct Menu *menu, int line)
946 {
947  struct Mailbox *m = ctx_mailbox(Context);
948  if (!m || (line < 0))
949  return 0;
950 
951  struct Email *e = mutt_get_virt_email(m, line);
952  if (!e)
953  return 0;
954 
955  if (e->pair)
956  return e->pair;
957 
958  mutt_set_header_color(m, e);
959  return e->pair;
960 }
961 
974 void mutt_draw_statusline(int cols, const char *buf, size_t buflen)
975 {
976  if (!buf || !stdscr)
977  return;
978 
979  size_t i = 0;
980  size_t offset = 0;
981  bool found = false;
982  size_t chunks = 0;
983  size_t len = 0;
984 
985  struct StatusSyntax
986  {
987  int color;
988  int first;
989  int last;
990  } *syntax = NULL;
991 
992  do
993  {
994  struct ColorLine *cl = NULL;
995  found = false;
996 
997  if (!buf[offset])
998  break;
999 
1000  /* loop through each "color status regex" */
1001  STAILQ_FOREACH(cl, &Colors->status_list, entries)
1002  {
1003  regmatch_t pmatch[cl->match + 1];
1004 
1005  if (regexec(&cl->regex, buf + offset, cl->match + 1, pmatch, 0) != 0)
1006  continue; /* regex doesn't match the status bar */
1007 
1008  int first = pmatch[cl->match].rm_so + offset;
1009  int last = pmatch[cl->match].rm_eo + offset;
1010 
1011  if (first == last)
1012  continue; /* ignore an empty regex */
1013 
1014  if (!found)
1015  {
1016  chunks++;
1017  mutt_mem_realloc(&syntax, chunks * sizeof(struct StatusSyntax));
1018  }
1019 
1020  i = chunks - 1;
1021  if (!found || (first < syntax[i].first) ||
1022  ((first == syntax[i].first) && (last > syntax[i].last)))
1023  {
1024  syntax[i].color = cl->pair;
1025  syntax[i].first = first;
1026  syntax[i].last = last;
1027  }
1028  found = true;
1029  }
1030 
1031  if (syntax)
1032  {
1033  offset = syntax[i].last;
1034  }
1035  } while (found);
1036 
1037  /* Only 'len' bytes will fit into 'cols' screen columns */
1038  len = mutt_wstr_trunc(buf, buflen, cols, NULL);
1039 
1040  offset = 0;
1041 
1042  if ((chunks > 0) && (syntax[0].first > 0))
1043  {
1044  /* Text before the first highlight */
1045  mutt_window_addnstr(buf, MIN(len, syntax[0].first));
1046  attrset(Colors->defs[MT_COLOR_STATUS]);
1047  if (len <= syntax[0].first)
1048  goto dsl_finish; /* no more room */
1049 
1050  offset = syntax[0].first;
1051  }
1052 
1053  for (i = 0; i < chunks; i++)
1054  {
1055  /* Highlighted text */
1056  attrset(syntax[i].color);
1057  mutt_window_addnstr(buf + offset, MIN(len, syntax[i].last) - offset);
1058  if (len <= syntax[i].last)
1059  goto dsl_finish; /* no more room */
1060 
1061  size_t next;
1062  if ((i + 1) == chunks)
1063  {
1064  next = len;
1065  }
1066  else
1067  {
1068  next = MIN(len, syntax[i + 1].first);
1069  }
1070 
1071  attrset(Colors->defs[MT_COLOR_STATUS]);
1072  offset = syntax[i].last;
1073  mutt_window_addnstr(buf + offset, next - offset);
1074 
1075  offset = next;
1076  if (offset >= len)
1077  goto dsl_finish; /* no more room */
1078  }
1079 
1080  attrset(Colors->defs[MT_COLOR_STATUS]);
1081  if (offset < len)
1082  {
1083  /* Text after the last highlight */
1084  mutt_window_addnstr(buf + offset, len - offset);
1085  }
1086 
1087  int width = mutt_strwidth(buf);
1088  if (width < cols)
1089  {
1090  /* Pad the rest of the line with whitespace */
1091  mutt_paddstr(cols - width, "");
1092  }
1093 dsl_finish:
1094  FREE(&syntax);
1095 }
1096 
1100 static void index_custom_redraw(struct Menu *menu)
1101 {
1102  if (menu->redraw & REDRAW_FULL)
1103  {
1104  menu_redraw_full(menu);
1105  mutt_show_error();
1106  }
1107 
1108  struct Mailbox *m = ctx_mailbox(Context);
1109  if (m && m->emails && !(menu->current >= m->vcount))
1110  {
1111  menu_check_recenter(menu);
1112 
1113  if (menu->redraw & REDRAW_INDEX)
1114  {
1115  menu_redraw_index(menu);
1116  menu->redraw |= REDRAW_STATUS;
1117  }
1118  else if (menu->redraw & (REDRAW_MOTION_RESYNC | REDRAW_MOTION))
1119  menu_redraw_motion(menu);
1120  else if (menu->redraw & REDRAW_CURRENT)
1121  menu_redraw_current(menu);
1122  }
1123 
1124  if (menu->redraw & REDRAW_STATUS)
1125  {
1126  char buf[1024];
1127  const char *c_status_format =
1128  cs_subset_string(NeoMutt->sub, "status_format");
1129  menu_status_line(buf, sizeof(buf), menu, m, NONULL(c_status_format));
1130  mutt_window_move(menu->win_ibar, 0, 0);
1132  mutt_draw_statusline(menu->win_ibar->state.cols, buf, sizeof(buf));
1134  menu->redraw &= ~REDRAW_STATUS;
1135  const bool c_ts_enabled = cs_subset_bool(NeoMutt->sub, "ts_enabled");
1136  if (c_ts_enabled && TsSupported)
1137  {
1138  const char *c_ts_status_format =
1139  cs_subset_string(NeoMutt->sub, "ts_status_format");
1140  menu_status_line(buf, sizeof(buf), menu, m, NONULL(c_ts_status_format));
1141  mutt_ts_status(buf);
1142  const char *c_ts_icon_format =
1143  cs_subset_string(NeoMutt->sub, "ts_icon_format");
1144  menu_status_line(buf, sizeof(buf), menu, m, NONULL(c_ts_icon_format));
1145  mutt_ts_icon(buf);
1146  }
1147  }
1148 
1149  menu->redraw = REDRAW_NO_FLAGS;
1150 }
1151 
1160 int mutt_index_menu(struct MuttWindow *dlg)
1161 {
1162  int op = OP_NULL;
1163  bool done = false; /* controls when to exit the "event" loop */
1164  bool tag = false; /* has the tag-prefix command been pressed? */
1165  int newcount = -1;
1166  int oldcount = -1;
1167  struct CurrentEmail cur = { 0 };
1168  bool do_mailbox_notify = true;
1169  int close = 0; /* did we OP_QUIT or OP_EXIT out of this menu? */
1170  int attach_msg = OptAttachMsg;
1171  bool in_pager = false; /* set when pager redirects a function through the index */
1172 
1173  struct MuttWindow *win_index = mutt_window_find(dlg, WT_INDEX);
1174  struct MuttWindow *win_ibar = mutt_window_find(dlg, WT_INDEX_BAR);
1175  struct MuttWindow *win_pager = mutt_window_find(dlg, WT_PAGER);
1176  struct MuttWindow *win_pbar = mutt_window_find(dlg, WT_PAGER_BAR);
1177 
1178 #ifdef USE_NNTP
1180  dlg->help_data = IndexNewsHelp;
1181  else
1182 #endif
1183  dlg->help_data = IndexHelp;
1184  dlg->help_menu = MENU_MAIN;
1185 
1186  struct Menu *menu = mutt_menu_new(MENU_MAIN);
1187  menu->pagelen = win_index->state.rows;
1188  menu->win_index = win_index;
1189  menu->win_ibar = win_ibar;
1190 
1191  menu->make_entry = index_make_entry;
1192  menu->color = index_color;
1195  mutt_menu_push_current(menu);
1196  mutt_window_reflow(NULL);
1197 
1198  if (!attach_msg)
1199  {
1200  /* force the mailbox check after we enter the folder */
1202  }
1203 #ifdef USE_INOTIFY
1204  mutt_monitor_add(NULL);
1205 #endif
1206 
1207  {
1208  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
1209  const bool c_collapse_all = cs_subset_bool(NeoMutt->sub, "collapse_all");
1210  if (((c_sort & SORT_MASK) == SORT_THREADS) && c_collapse_all)
1211  {
1212  collapse_all(Context, menu, 0);
1213  menu->redraw = REDRAW_FULL;
1214  }
1215  }
1216 
1217  while (true)
1218  {
1219  /* Clear the tag prefix unless we just started it. Don't clear
1220  * the prefix on a timeout (op==-2), but do clear on an abort (op==-1) */
1221  if (tag && (op != OP_TAG_PREFIX) && (op != OP_TAG_PREFIX_COND) && (op != -2))
1222  tag = false;
1223 
1224  /* check if we need to resort the index because just about
1225  * any 'op' below could do mutt_enter_command(), either here or
1226  * from any new menu launched, and change $sort/$sort_aux */
1227  if (OptNeedResort && ctx_mailbox(Context) &&
1228  (Context->mailbox->msg_count != 0) && (menu->current >= 0))
1229  {
1230  resort_index(Context, menu);
1231  }
1232 
1233  menu->max = ctx_mailbox(Context) ? Context->mailbox->vcount : 0;
1234  oldcount = ctx_mailbox(Context) ? Context->mailbox->msg_count : 0;
1235 
1236  {
1237  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
1238  if (OptRedrawTree && ctx_mailbox(Context) &&
1239  (Context->mailbox->msg_count != 0) && ((c_sort & SORT_MASK) == SORT_THREADS))
1240  {
1242  menu->redraw |= REDRAW_STATUS;
1243  OptRedrawTree = false;
1244  }
1245  }
1246 
1247  if (ctx_mailbox(Context))
1248  {
1249  mailbox_gc_run();
1250 
1251  Context->menu = menu;
1252  /* check for new mail in the mailbox. If nonzero, then something has
1253  * changed about the file (either we got new mail or the file was
1254  * modified underneath us.) */
1255  enum MxStatus check = mx_mbox_check(Context->mailbox);
1256 
1257  if (check == MX_STATUS_ERROR)
1258  {
1260  {
1261  /* fatal error occurred */
1262  ctx_free(&Context);
1263  menu->redraw = REDRAW_FULL;
1264  }
1265 
1266  OptSearchInvalid = true;
1267  }
1268  else if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED) ||
1269  (check == MX_STATUS_FLAGS))
1270  {
1271  /* notify the user of new mail */
1272  if (check == MX_STATUS_REOPENED)
1273  {
1274  mutt_error(
1275  _("Mailbox was externally modified. Flags may be wrong."));
1276  }
1277  else if (check == MX_STATUS_NEW_MAIL)
1278  {
1279  for (size_t i = 0; i < Context->mailbox->msg_count; i++)
1280  {
1281  const struct Email *e = Context->mailbox->emails[i];
1282  if (e && !e->read && !e->old)
1283  {
1284  mutt_message(_("New mail in this mailbox"));
1285  const bool c_beep_new = cs_subset_bool(NeoMutt->sub, "beep_new");
1286  if (c_beep_new)
1287  mutt_beep(true);
1288  const char *c_new_mail_command =
1289  cs_subset_string(NeoMutt->sub, "new_mail_command");
1290  if (c_new_mail_command)
1291  {
1292  char cmd[1024];
1293  menu_status_line(cmd, sizeof(cmd), menu, Context->mailbox,
1294  NONULL(c_new_mail_command));
1295  if (mutt_system(cmd) != 0)
1296  mutt_error(_("Error running \"%s\""), cmd);
1297  }
1298  break;
1299  }
1300  }
1301  }
1302  else if (check == MX_STATUS_FLAGS)
1303  {
1304  mutt_message(_("Mailbox was externally modified"));
1305  }
1306 
1307  /* avoid the message being overwritten by mailbox */
1308  do_mailbox_notify = false;
1309 
1310  bool verbose = Context->mailbox->verbose;
1311  Context->mailbox->verbose = false;
1312  update_index(menu, Context, check, oldcount, &cur);
1313  Context->mailbox->verbose = verbose;
1314  menu->max = Context->mailbox->vcount;
1315  menu->redraw = REDRAW_FULL;
1316  OptSearchInvalid = true;
1317  }
1318 
1319  if (Context)
1320  {
1322  }
1323  }
1324 
1325  if (!attach_msg)
1326  {
1327  struct Mailbox *m = Context ? Context->mailbox : NULL;
1328  /* check for new mail in the incoming folders */
1329  oldcount = newcount;
1330  newcount = mutt_mailbox_check(m, 0);
1331  if (newcount != oldcount)
1332  menu->redraw |= REDRAW_STATUS;
1333  if (do_mailbox_notify)
1334  {
1335  if (mutt_mailbox_notify(m))
1336  {
1337  menu->redraw |= REDRAW_STATUS;
1338  const bool c_beep_new = cs_subset_bool(NeoMutt->sub, "beep_new");
1339  if (c_beep_new)
1340  mutt_beep(true);
1341  const char *c_new_mail_command =
1342  cs_subset_string(NeoMutt->sub, "new_mail_command");
1343  if (c_new_mail_command)
1344  {
1345  char cmd[1024];
1346  menu_status_line(cmd, sizeof(cmd), menu, m, NONULL(c_new_mail_command));
1347  if (mutt_system(cmd) != 0)
1348  mutt_error(_("Error running \"%s\""), cmd);
1349  }
1350  }
1351  }
1352  else
1353  do_mailbox_notify = true;
1354  }
1355 
1356  if (op >= 0)
1358 
1359  if (in_pager)
1360  {
1361  if (menu->current < menu->max)
1362  menu->oldcurrent = menu->current;
1363  else
1364  menu->oldcurrent = -1;
1365 
1366  mutt_curses_set_cursor(MUTT_CURSOR_VISIBLE); /* fallback from the pager */
1367  }
1368  else
1369  {
1370  index_custom_redraw(menu);
1371  window_redraw(RootWindow, false);
1372 
1373  /* give visual indication that the next command is a tag- command */
1374  if (tag)
1375  {
1376  mutt_window_mvaddstr(MessageWindow, 0, 0, "tag-");
1378  }
1379 
1380  if (menu->current < menu->max)
1381  menu->oldcurrent = menu->current;
1382  else
1383  menu->oldcurrent = -1;
1384 
1385  const bool c_arrow_cursor = cs_subset_bool(NeoMutt->sub, "arrow_cursor");
1386  const bool c_braille_friendly =
1387  cs_subset_bool(NeoMutt->sub, "braille_friendly");
1388  if (c_arrow_cursor)
1389  mutt_window_move(menu->win_index, 2, menu->current - menu->top);
1390  else if (c_braille_friendly)
1391  mutt_window_move(menu->win_index, 0, menu->current - menu->top);
1392  else
1393  {
1394  mutt_window_move(menu->win_index, menu->win_index->state.cols - 1,
1395  menu->current - menu->top);
1396  }
1397  mutt_refresh();
1398 
1399  if (SigWinch)
1400  {
1401  SigWinch = 0;
1403  menu->top = 0; /* so we scroll the right amount */
1404  /* force a real complete redraw. clrtobot() doesn't seem to be able
1405  * to handle every case without this. */
1406  clearok(stdscr, true);
1408  continue;
1409  }
1410 
1411  op = km_dokey(MENU_MAIN);
1412 
1413  /* either user abort or timeout */
1414  if (op < 0)
1415  {
1417  if (tag)
1419  continue;
1420  }
1421 
1422  mutt_debug(LL_DEBUG1, "Got op %s (%d)\n", OpStrings[op][0], op);
1423 
1425 
1426  /* special handling for the tag-prefix function */
1427  const bool c_auto_tag = cs_subset_bool(NeoMutt->sub, "auto_tag");
1428  if ((op == OP_TAG_PREFIX) || (op == OP_TAG_PREFIX_COND))
1429  {
1430  /* A second tag-prefix command aborts */
1431  if (tag)
1432  {
1433  tag = false;
1435  continue;
1436  }
1437 
1438  if (!ctx_mailbox(Context))
1439  {
1440  mutt_error(_("No mailbox is open"));
1441  continue;
1442  }
1443 
1444  if (Context->mailbox->msg_tagged == 0)
1445  {
1446  if (op == OP_TAG_PREFIX)
1447  mutt_error(_("No tagged messages"));
1448  else if (op == OP_TAG_PREFIX_COND)
1449  {
1451  mutt_message(_("Nothing to do"));
1452  }
1453  continue;
1454  }
1455 
1456  /* get the real command */
1457  tag = true;
1458  continue;
1459  }
1460  else if (c_auto_tag && ctx_mailbox(Context) && (Context->mailbox->msg_tagged != 0))
1461  {
1462  tag = true;
1463  }
1464 
1465  mutt_clear_error();
1466  }
1467 
1468 #ifdef USE_NNTP
1469  OptNews = false; /* for any case */
1470 #endif
1471 
1472 #ifdef USE_NOTMUCH
1473  if (Context)
1475 #endif
1476 
1477  switch (op)
1478  {
1479  /* ----------------------------------------------------------------------
1480  * movement commands
1481  */
1482 
1483  case OP_BOTTOM_PAGE:
1484  menu_bottom_page(menu);
1485  break;
1486  case OP_CURRENT_BOTTOM:
1487  menu_current_bottom(menu);
1488  break;
1489  case OP_CURRENT_MIDDLE:
1490  menu_current_middle(menu);
1491  break;
1492  case OP_CURRENT_TOP:
1493  menu_current_top(menu);
1494  break;
1495  case OP_FIRST_ENTRY:
1496  menu_first_entry(menu);
1497  break;
1498  case OP_HALF_DOWN:
1499  menu_half_down(menu);
1500  break;
1501  case OP_HALF_UP:
1502  menu_half_up(menu);
1503  break;
1504  case OP_LAST_ENTRY:
1505  menu_last_entry(menu);
1506  break;
1507  case OP_MIDDLE_PAGE:
1508  menu_middle_page(menu);
1509  break;
1510  case OP_NEXT_LINE:
1511  menu_next_line(menu);
1512  break;
1513  case OP_NEXT_PAGE:
1514  menu_next_page(menu);
1515  break;
1516  case OP_PREV_LINE:
1517  menu_prev_line(menu);
1518  break;
1519  case OP_PREV_PAGE:
1520  menu_prev_page(menu);
1521  break;
1522  case OP_TOP_PAGE:
1523  menu_top_page(menu);
1524  break;
1525 
1526 #ifdef USE_NNTP
1527  case OP_GET_PARENT:
1529  break;
1530  /* fallthrough */
1531 
1532  case OP_GET_MESSAGE:
1533  {
1535  break;
1536  char buf[PATH_MAX] = { 0 };
1537  if (Context->mailbox->type == MUTT_NNTP)
1538  {
1539  if (op == OP_GET_MESSAGE)
1540  {
1541  if ((mutt_get_field(_("Enter Message-Id: "), buf, sizeof(buf),
1542  MUTT_COMP_NO_FLAGS, false, NULL, NULL) != 0) ||
1543  (buf[0] == '\0'))
1544  {
1545  break;
1546  }
1547  }
1548  else
1549  {
1550  if (!cur.e || STAILQ_EMPTY(&cur.e->env->references))
1551  {
1552  mutt_error(_("Article has no parent reference"));
1553  break;
1554  }
1555  mutt_str_copy(buf, STAILQ_FIRST(&cur.e->env->references)->data, sizeof(buf));
1556  }
1557  if (!Context->mailbox->id_hash)
1559  struct Email *e = mutt_hash_find(Context->mailbox->id_hash, buf);
1560  if (e)
1561  {
1562  if (e->vnum != -1)
1563  {
1564  menu->current = e->vnum;
1565  menu->redraw = REDRAW_MOTION_RESYNC;
1566  }
1567  else if (e->collapsed)
1568  {
1571  menu->current = e->vnum;
1572  menu->redraw = REDRAW_MOTION_RESYNC;
1573  }
1574  else
1575  mutt_error(_("Message is not visible in limited view"));
1576  }
1577  else
1578  {
1579  mutt_message(_("Fetching %s from server..."), buf);
1580  int rc = nntp_check_msgid(Context->mailbox, buf);
1581  if (rc == 0)
1582  {
1585  &Context->vsize);
1586  menu->current = e->vnum;
1587  menu->redraw = REDRAW_FULL;
1588  }
1589  else if (rc > 0)
1590  mutt_error(_("Article %s not found on the server"), buf);
1591  }
1592  }
1593  break;
1594  }
1595 
1596  case OP_GET_CHILDREN:
1597  case OP_RECONSTRUCT_THREAD:
1598  {
1599  if (!prereq(Context, menu,
1601  {
1602  break;
1603  }
1604  if (Context->mailbox->type != MUTT_NNTP)
1605  break;
1606 
1607  if (!cur.e)
1608  break;
1609 
1610  char buf[PATH_MAX] = { 0 };
1611  int oldmsgcount = Context->mailbox->msg_count;
1612  int oldindex = cur.e->index;
1613  int rc = 0;
1614 
1615  if (!cur.e->env->message_id)
1616  {
1617  mutt_error(_("No Message-Id. Unable to perform operation."));
1618  break;
1619  }
1620 
1621  mutt_message(_("Fetching message headers..."));
1622  if (!Context->mailbox->id_hash)
1624  mutt_str_copy(buf, cur.e->env->message_id, sizeof(buf));
1625 
1626  /* trying to find msgid of the root message */
1627  if (op == OP_RECONSTRUCT_THREAD)
1628  {
1629  struct ListNode *ref = NULL;
1630  STAILQ_FOREACH(ref, &cur.e->env->references, entries)
1631  {
1632  if (!mutt_hash_find(Context->mailbox->id_hash, ref->data))
1633  {
1634  rc = nntp_check_msgid(Context->mailbox, ref->data);
1635  if (rc < 0)
1636  break;
1637  }
1638 
1639  /* the last msgid in References is the root message */
1640  if (!STAILQ_NEXT(ref, entries))
1641  mutt_str_copy(buf, ref->data, sizeof(buf));
1642  }
1643  }
1644 
1645  /* fetching all child messages */
1646  if (rc >= 0)
1647  rc = nntp_check_children(Context->mailbox, buf);
1648 
1649  /* at least one message has been loaded */
1650  if (Context->mailbox->msg_count > oldmsgcount)
1651  {
1652  struct Email *e_oldcur = mutt_get_virt_email(Context->mailbox, menu->current);
1653  bool verbose = Context->mailbox->verbose;
1654 
1655  if (rc < 0)
1656  Context->mailbox->verbose = false;
1658  (op == OP_RECONSTRUCT_THREAD), &Context->vsize);
1659  Context->mailbox->verbose = verbose;
1660 
1661  /* Similar to OP_MAIN_ENTIRE_THREAD, keep displaying the old message, but
1662  * update the index */
1663  if (in_pager)
1664  {
1665  menu->current = e_oldcur->vnum;
1666  menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1667  op = OP_DISPLAY_MESSAGE;
1668  continue;
1669  }
1670 
1671  /* if the root message was retrieved, move to it */
1672  struct Email *e = mutt_hash_find(Context->mailbox->id_hash, buf);
1673  if (e)
1674  menu->current = e->vnum;
1675  else
1676  {
1677  /* try to restore old position */
1678  for (int i = 0; i < Context->mailbox->msg_count; i++)
1679  {
1680  e = Context->mailbox->emails[i];
1681  if (!e)
1682  break;
1683  if (e->index == oldindex)
1684  {
1685  menu->current = e->vnum;
1686  /* as an added courtesy, recenter the menu
1687  * with the current entry at the middle of the screen */
1688  menu_check_recenter(menu);
1689  menu_current_middle(menu);
1690  }
1691  }
1692  }
1693  menu->redraw = REDRAW_FULL;
1694  }
1695  else if (rc >= 0)
1696  {
1697  mutt_error(_("No deleted messages found in the thread"));
1698  /* Similar to OP_MAIN_ENTIRE_THREAD, keep displaying the old message, but
1699  * update the index */
1700  if (in_pager)
1701  {
1702  op = OP_DISPLAY_MESSAGE;
1703  continue;
1704  }
1705  }
1706  break;
1707  }
1708 #endif
1709 
1710  case OP_JUMP:
1711  {
1712  if (!prereq(Context, menu, CHECK_IN_MAILBOX))
1713  break;
1714  char buf[PATH_MAX] = { 0 };
1715  int msg_num = 0;
1716  if (isdigit(LastKey))
1718  if ((mutt_get_field(_("Jump to message: "), buf, sizeof(buf),
1719  MUTT_COMP_NO_FLAGS, false, NULL, NULL) != 0) ||
1720  (buf[0] == '\0'))
1721  {
1722  mutt_error(_("Nothing to do"));
1723  }
1724  else if (mutt_str_atoi(buf, &msg_num) < 0)
1725  mutt_error(_("Argument must be a message number"));
1726  else if ((msg_num < 1) || (msg_num > Context->mailbox->msg_count))
1727  mutt_error(_("Invalid message number"));
1728  else if (!Context->mailbox->emails[msg_num - 1]->visible)
1729  mutt_error(_("That message is not visible"));
1730  else
1731  {
1732  struct Email *e = Context->mailbox->emails[msg_num - 1];
1733 
1735  {
1738  }
1739  menu->current = e->vnum;
1740  }
1741 
1742  if (in_pager)
1743  {
1744  op = OP_DISPLAY_MESSAGE;
1745  continue;
1746  }
1747  else
1748  menu->redraw = REDRAW_FULL;
1749 
1750  break;
1751  }
1752 
1753  /* --------------------------------------------------------------------
1754  * 'index' specific commands
1755  */
1756 
1757  case OP_MAIN_DELETE_PATTERN:
1759  {
1760  break;
1761  }
1762  /* L10N: CHECK_ACL */
1763  /* L10N: Due to the implementation details we do not know whether we
1764  delete zero, 1, 12, ... messages. So in English we use
1765  "messages". Your language might have other means to express this. */
1766  if (!check_acl(Context->mailbox, MUTT_ACL_DELETE, _("Can't delete messages")))
1767  break;
1768 
1769  mutt_pattern_func(Context, MUTT_DELETE, _("Delete messages matching: "));
1770  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
1771  break;
1772 
1773 #ifdef USE_POP
1774  case OP_MAIN_FETCH_MAIL:
1775  if (!prereq(Context, menu, CHECK_ATTACH))
1776  break;
1777  pop_fetch_mail();
1778  menu->redraw = REDRAW_FULL;
1779  break;
1780 #endif /* USE_POP */
1781 
1782  case OP_SHOW_LOG_MESSAGES:
1783  {
1784 #ifdef USE_DEBUG_GRAPHVIZ
1785  dump_graphviz("index", Context);
1786 #endif
1787  char tempfile[PATH_MAX];
1788  mutt_mktemp(tempfile, sizeof(tempfile));
1789 
1790  FILE *fp = mutt_file_fopen(tempfile, "a+");
1791  if (!fp)
1792  {
1793  mutt_perror("fopen");
1794  break;
1795  }
1796 
1797  log_queue_save(fp);
1798  mutt_file_fclose(&fp);
1799 
1800  mutt_do_pager("messages", tempfile, MUTT_PAGER_LOGS, NULL);
1801  break;
1802  }
1803 
1804  case OP_HELP:
1806  menu->redraw = REDRAW_FULL;
1807  break;
1808 
1809  case OP_MAIN_SHOW_LIMIT:
1810  if (!prereq(Context, menu, CHECK_IN_MAILBOX))
1811  break;
1812  if (!ctx_has_limit(Context))
1813  mutt_message(_("No limit pattern is in effect"));
1814  else
1815  {
1816  char buf2[256];
1817  /* L10N: ask for a limit to apply */
1818  snprintf(buf2, sizeof(buf2), _("Limit: %s"), Context->pattern);
1819  mutt_message("%s", buf2);
1820  }
1821  break;
1822 
1823  case OP_LIMIT_CURRENT_THREAD:
1824  case OP_MAIN_LIMIT:
1825  case OP_TOGGLE_READ:
1826  {
1827  if (!prereq(Context, menu, CHECK_IN_MAILBOX))
1828  break;
1829  const bool lmt = ctx_has_limit(Context);
1830  menu->oldcurrent = cur.e ? cur.e->index : -1;
1831  if (op == OP_TOGGLE_READ)
1832  {
1833  char buf2[1024];
1834 
1835  if (!lmt || !mutt_strn_equal(Context->pattern, "!~R!~D~s", 8))
1836  {
1837  snprintf(buf2, sizeof(buf2), "!~R!~D~s%s", lmt ? Context->pattern : ".*");
1838  }
1839  else
1840  {
1841  mutt_str_copy(buf2, Context->pattern + 8, sizeof(buf2));
1842  if ((*buf2 == '\0') || mutt_strn_equal(buf2, ".*", 2))
1843  snprintf(buf2, sizeof(buf2), "~A");
1844  }
1845  mutt_str_replace(&Context->pattern, buf2);
1847  }
1848 
1849  if (((op == OP_LIMIT_CURRENT_THREAD) && mutt_limit_current_thread(Context, cur.e)) ||
1850  (op == OP_TOGGLE_READ) ||
1851  ((op == OP_MAIN_LIMIT) &&
1852  (mutt_pattern_func(Context, MUTT_LIMIT, _("Limit to messages matching: ")) == 0)))
1853  {
1854  if (menu->oldcurrent >= 0)
1855  {
1856  /* try to find what used to be the current message */
1857  menu->current = -1;
1858  for (size_t i = 0; i < Context->mailbox->vcount; i++)
1859  {
1860  struct Email *e = mutt_get_virt_email(Context->mailbox, i);
1861  if (!e)
1862  continue;
1863  if (e->index == menu->oldcurrent)
1864  {
1865  menu->current = i;
1866  break;
1867  }
1868  }
1869  if (menu->current < 0)
1870  menu->current = 0;
1871  }
1872  else
1873  menu->current = 0;
1874  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
1875  if ((Context->mailbox->msg_count != 0) && ((c_sort & SORT_MASK) == SORT_THREADS))
1876  {
1877  const bool c_collapse_all =
1878  cs_subset_bool(NeoMutt->sub, "collapse_all");
1879  if (c_collapse_all)
1880  collapse_all(Context, menu, 0);
1882  }
1883  menu->redraw = REDRAW_FULL;
1884  }
1885  if (lmt)
1886  mutt_message(_("To view all messages, limit to \"all\""));
1887  break;
1888  }
1889 
1890  case OP_QUIT:
1891  {
1892  close = op;
1893  if (attach_msg)
1894  {
1895  done = true;
1896  break;
1897  }
1898 
1899  const enum QuadOption c_quit = cs_subset_quad(NeoMutt->sub, "quit");
1900  if (query_quadoption(c_quit, _("Quit NeoMutt?")) == MUTT_YES)
1901  {
1902  oldcount = (Context && Context->mailbox) ? Context->mailbox->msg_count : 0;
1903 
1906 
1907  enum MxStatus check = MX_STATUS_OK;
1908  if (!Context || ((check = mx_mbox_close(&Context)) == MX_STATUS_OK))
1909  {
1910  done = true;
1911  }
1912  else
1913  {
1914  if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
1915  {
1916  update_index(menu, Context, check, oldcount, &cur);
1917  }
1918 
1919  menu->redraw = REDRAW_FULL; /* new mail arrived? */
1920  OptSearchInvalid = true;
1921  }
1922  }
1923  break;
1924  }
1925 
1926  case OP_REDRAW:
1927  mutt_window_reflow(NULL);
1928  clearok(stdscr, true);
1929  menu->redraw = REDRAW_FULL;
1930  break;
1931 
1932  // Initiating a search can happen on an empty mailbox, but
1933  // searching for next/previous/... needs to be on a message and
1934  // thus a non-empty mailbox
1935  case OP_SEARCH_REVERSE:
1936  case OP_SEARCH_NEXT:
1937  case OP_SEARCH_OPPOSITE:
1939  break;
1940  /* fallthrough */
1941  case OP_SEARCH:
1942  if (!prereq(Context, menu, CHECK_IN_MAILBOX))
1943  break;
1944  menu->current = mutt_search_command(Context, Context->mailbox, menu->current, op);
1945  if (menu->current == -1)
1946  menu->current = menu->oldcurrent;
1947  else
1948  menu->redraw |= REDRAW_MOTION;
1949  break;
1950 
1951  case OP_SORT:
1952  case OP_SORT_REVERSE:
1953  if (mutt_select_sort((op == OP_SORT_REVERSE)) != 0)
1954  break;
1955 
1956  if (ctx_mailbox(Context) && (Context->mailbox->msg_count != 0))
1957  {
1958  resort_index(Context, menu);
1959  OptSearchInvalid = true;
1960  }
1961  if (in_pager)
1962  {
1963  op = OP_DISPLAY_MESSAGE;
1964  continue;
1965  }
1966  menu->redraw |= REDRAW_STATUS;
1967  break;
1968 
1969  case OP_TAG:
1970  {
1972  break;
1973  const bool c_auto_tag = cs_subset_bool(NeoMutt->sub, "auto_tag");
1974  if (tag && !c_auto_tag)
1975  {
1976  struct Mailbox *m = Context->mailbox;
1977  for (size_t i = 0; i < m->msg_count; i++)
1978  {
1979  struct Email *e = m->emails[i];
1980  if (!e)
1981  break;
1982  if (e->visible)
1983  mutt_set_flag(m, e, MUTT_TAG, false);
1984  }
1985  menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
1986  }
1987  else
1988  {
1989  if (!cur.e)
1990  break;
1991  mutt_set_flag(Context->mailbox, cur.e, MUTT_TAG, !cur.e->tagged);
1992 
1993  menu->redraw |= REDRAW_STATUS;
1994  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
1995  if (c_resolve && (menu->current < Context->mailbox->vcount - 1))
1996  {
1997  menu->current++;
1998  menu->redraw |= REDRAW_MOTION_RESYNC;
1999  }
2000  else
2001  menu->redraw |= REDRAW_CURRENT;
2002  }
2003  break;
2004  }
2005 
2006  case OP_MAIN_TAG_PATTERN:
2007  if (!prereq(Context, menu, CHECK_IN_MAILBOX))
2008  break;
2009  mutt_pattern_func(Context, MUTT_TAG, _("Tag messages matching: "));
2010  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
2011  break;
2012 
2013  case OP_MAIN_UNDELETE_PATTERN:
2015  break;
2016  /* L10N: CHECK_ACL */
2017  /* L10N: Due to the implementation details we do not know whether we
2018  undelete zero, 1, 12, ... messages. So in English we use
2019  "messages". Your language might have other means to express this. */
2020  if (!check_acl(Context->mailbox, MUTT_ACL_DELETE, _("Can't undelete messages")))
2021  break;
2022 
2023  if (mutt_pattern_func(Context, MUTT_UNDELETE, _("Undelete messages matching: ")) == 0)
2024  {
2025  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
2026  }
2027  break;
2028 
2029  case OP_MAIN_UNTAG_PATTERN:
2030  if (!prereq(Context, menu, CHECK_IN_MAILBOX))
2031  break;
2032  if (mutt_pattern_func(Context, MUTT_UNTAG, _("Untag messages matching: ")) == 0)
2033  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
2034  break;
2035 
2036  case OP_COMPOSE_TO_SENDER:
2037  {
2039  break;
2040  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2041  el_add_tagged(&el, Context, cur.e, tag);
2042  mutt_send_message(SEND_TO_SENDER, NULL, NULL, Context, &el, NeoMutt->sub);
2043  emaillist_clear(&el);
2044  menu->redraw = REDRAW_FULL;
2045  break;
2046  }
2047 
2048  /* --------------------------------------------------------------------
2049  * The following operations can be performed inside of the pager.
2050  */
2051 
2052 #ifdef USE_IMAP
2053  case OP_MAIN_IMAP_FETCH:
2056  break;
2057 
2058  case OP_MAIN_IMAP_LOGOUT_ALL:
2060  {
2061  const enum MxStatus check = mx_mbox_close(&Context);
2062  if (check != MX_STATUS_OK)
2063  {
2064  if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
2065  {
2066  update_index(menu, Context, check, oldcount, &cur);
2067  }
2068  OptSearchInvalid = true;
2069  menu->redraw = REDRAW_FULL;
2070  break;
2071  }
2072  }
2073  imap_logout_all();
2074  mutt_message(_("Logged out of IMAP servers"));
2075  OptSearchInvalid = true;
2076  menu->redraw = REDRAW_FULL;
2077  break;
2078 #endif
2079 
2080  case OP_MAIN_SYNC_FOLDER:
2081  if (!ctx_mailbox(Context) || (Context->mailbox->msg_count == 0))
2082  break;
2083 
2085  break;
2086  {
2087  int ovc = Context->mailbox->vcount;
2088  int oc = Context->mailbox->msg_count;
2089  struct Email *e = NULL;
2090 
2091  /* don't attempt to move the cursor if there are no visible messages in the current limit */
2092  if (menu->current < Context->mailbox->vcount)
2093  {
2094  /* threads may be reordered, so figure out what header the cursor
2095  * should be on. */
2096  int newidx = menu->current;
2097  if (!cur.e)
2098  break;
2099  if (cur.e->deleted)
2100  newidx = ci_next_undeleted(Context->mailbox, menu->current);
2101  if (newidx < 0)
2102  newidx = ci_previous_undeleted(Context->mailbox, menu->current);
2103  if (newidx >= 0)
2104  e = mutt_get_virt_email(Context->mailbox, newidx);
2105  }
2106 
2107  enum MxStatus check = mx_mbox_sync(Context->mailbox);
2108  if (check == MX_STATUS_OK)
2109  {
2110  if (e && (Context->mailbox->vcount != ovc))
2111  {
2112  for (size_t i = 0; i < Context->mailbox->vcount; i++)
2113  {
2114  struct Email *e2 = mutt_get_virt_email(Context->mailbox, i);
2115  if (e2 == e)
2116  {
2117  menu->current = i;
2118  break;
2119  }
2120  }
2121  }
2122  OptSearchInvalid = true;
2123  }
2124  else if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
2125  {
2126  update_index(menu, Context, check, oc, &cur);
2127  }
2128 
2129  /* do a sanity check even if mx_mbox_sync failed. */
2130 
2131  if ((menu->current < 0) ||
2132  (ctx_mailbox(Context) && (menu->current >= Context->mailbox->vcount)))
2133  {
2135  }
2136  }
2137 
2138  /* check for a fatal error, or all messages deleted */
2140  ctx_free(&Context);
2141 
2142  /* if we were in the pager, redisplay the message */
2143  if (in_pager)
2144  {
2145  op = OP_DISPLAY_MESSAGE;
2146  continue;
2147  }
2148  else
2149  menu->redraw = REDRAW_FULL;
2150  break;
2151 
2152  case OP_MAIN_QUASI_DELETE:
2154  break;
2155  if (tag)
2156  {
2157  struct Mailbox *m = Context->mailbox;
2158  for (size_t i = 0; i < m->msg_count; i++)
2159  {
2160  struct Email *e = m->emails[i];
2161  if (!e)
2162  break;
2163  if (message_is_tagged(Context, e))
2164  {
2165  e->quasi_deleted = true;
2166  m->changed = true;
2167  }
2168  }
2169  }
2170  else
2171  {
2172  if (!cur.e)
2173  break;
2174  cur.e->quasi_deleted = true;
2175  Context->mailbox->changed = true;
2176  }
2177  break;
2178 
2179 #ifdef USE_NOTMUCH
2180  case OP_MAIN_ENTIRE_THREAD:
2181  {
2183  break;
2184  char buf[PATH_MAX] = { 0 };
2185  if (Context->mailbox->type != MUTT_NOTMUCH)
2186  {
2187  if (((Context->mailbox->type != MUTT_MH) && (Context->mailbox->type != MUTT_MAILDIR)) ||
2188  (!cur.e || !cur.e->env || !cur.e->env->message_id))
2189  {
2190  mutt_message(_("No virtual folder and no Message-Id, aborting"));
2191  break;
2192  } // no virtual folder, but we have message-id, reconstruct thread on-the-fly
2193  strncpy(buf, "id:", sizeof(buf));
2194  int msg_id_offset = 0;
2195  if ((cur.e->env->message_id)[0] == '<')
2196  msg_id_offset = 1;
2197  mutt_str_cat(buf, sizeof(buf), (cur.e->env->message_id) + msg_id_offset);
2198  if (buf[strlen(buf) - 1] == '>')
2199  buf[strlen(buf) - 1] = '\0';
2200 
2201  change_folder_notmuch(menu, buf, sizeof(buf), &oldcount, &cur, false);
2202 
2203  // If notmuch doesn't contain the message, we're left in an empty
2204  // vfolder. No messages are found, but nm_read_entire_thread assumes
2205  // a valid message-id and will throw a segfault.
2206  //
2207  // To prevent that, stay in the empty vfolder and print an error.
2208  if (Context->mailbox->msg_count == 0)
2209  {
2210  mutt_error(_("failed to find message in notmuch database. try "
2211  "running 'notmuch new'."));
2212  break;
2213  }
2214  }
2215  oldcount = Context->mailbox->msg_count;
2216  struct Email *e_oldcur = mutt_get_virt_email(Context->mailbox, menu->current);
2217  if (nm_read_entire_thread(Context->mailbox, e_oldcur) < 0)
2218  {
2219  mutt_message(_("Failed to read thread, aborting"));
2220  break;
2221  }
2222  if (oldcount < Context->mailbox->msg_count)
2223  {
2224  /* nm_read_entire_thread() triggers mutt_sort_headers() if necessary */
2225  menu->current = e_oldcur->vnum;
2226  menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
2227 
2228  if (e_oldcur->collapsed || Context->collapsed)
2229  {
2230  menu->current = mutt_uncollapse_thread(e_oldcur);
2232  }
2233  }
2234  if (in_pager)
2235  {
2236  op = OP_DISPLAY_MESSAGE;
2237  continue;
2238  }
2239  break;
2240  }
2241 #endif
2242 
2243  case OP_MAIN_MODIFY_TAGS:
2244  case OP_MAIN_MODIFY_TAGS_THEN_HIDE:
2245  {
2246  if (!ctx_mailbox(Context))
2247  break;
2248  struct Mailbox *m = Context->mailbox;
2249  if (!mx_tags_is_supported(m))
2250  {
2251  mutt_message(_("Folder doesn't support tagging, aborting"));
2252  break;
2253  }
2255  break;
2256  if (!cur.e)
2257  break;
2258  char *tags = NULL;
2259  if (!tag)
2260  tags = driver_tags_get_with_hidden(&cur.e->tags);
2261  char buf[PATH_MAX] = { 0 };
2262  int rc = mx_tags_edit(m, tags, buf, sizeof(buf));
2263  FREE(&tags);
2264  if (rc < 0)
2265  break;
2266  else if (rc == 0)
2267  {
2268  mutt_message(_("No tag specified, aborting"));
2269  break;
2270  }
2271 
2272  if (tag)
2273  {
2274  struct Progress progress;
2275 
2276  if (m->verbose)
2277  {
2278  mutt_progress_init(&progress, _("Update tags..."),
2280  }
2281 
2282 #ifdef USE_NOTMUCH
2283  if (m->type == MUTT_NOTMUCH)
2284  nm_db_longrun_init(m, true);
2285 #endif
2286  for (int px = 0, i = 0; i < m->msg_count; i++)
2287  {
2288  struct Email *e = m->emails[i];
2289  if (!e)
2290  break;
2291  if (!message_is_tagged(Context, e))
2292  continue;
2293 
2294  if (m->verbose)
2295  mutt_progress_update(&progress, ++px, -1);
2296  mx_tags_commit(m, e, buf);
2297  if (op == OP_MAIN_MODIFY_TAGS_THEN_HIDE)
2298  {
2299  bool still_queried = false;
2300 #ifdef USE_NOTMUCH
2301  if (m->type == MUTT_NOTMUCH)
2302  still_queried = nm_message_is_still_queried(m, e);
2303 #endif
2304  e->quasi_deleted = !still_queried;
2305  m->changed = true;
2306  }
2307  }
2308 #ifdef USE_NOTMUCH
2309  if (m->type == MUTT_NOTMUCH)
2310  nm_db_longrun_done(m);
2311 #endif
2312  menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
2313  }
2314  else
2315  {
2316  if (mx_tags_commit(m, cur.e, buf))
2317  {
2318  mutt_message(_("Failed to modify tags, aborting"));
2319  break;
2320  }
2321  if (op == OP_MAIN_MODIFY_TAGS_THEN_HIDE)
2322  {
2323  bool still_queried = false;
2324 #ifdef USE_NOTMUCH
2325  if (m->type == MUTT_NOTMUCH)
2326  still_queried = nm_message_is_still_queried(m, cur.e);
2327 #endif
2328  cur.e->quasi_deleted = !still_queried;
2329  m->changed = true;
2330  }
2331  if (in_pager)
2332  {
2333  op = OP_DISPLAY_MESSAGE;
2334  continue;
2335  }
2336  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
2337  if (c_resolve)
2338  {
2339  menu->current = ci_next_undeleted(Context->mailbox, menu->current);
2340  if (menu->current == -1)
2341  {
2342  menu->current = menu->oldcurrent;
2343  menu->redraw = REDRAW_CURRENT;
2344  }
2345  else
2346  menu->redraw = REDRAW_MOTION_RESYNC;
2347  }
2348  else
2349  menu->redraw = REDRAW_CURRENT;
2350  }
2351  menu->redraw |= REDRAW_STATUS;
2352  break;
2353  }
2354 
2355  case OP_CHECK_STATS:
2357  break;
2358 
2359 #ifdef USE_NOTMUCH
2360  case OP_MAIN_VFOLDER_FROM_QUERY:
2361  case OP_MAIN_VFOLDER_FROM_QUERY_READONLY:
2362  {
2363  char buf[PATH_MAX] = { 0 };
2364  if ((mutt_get_field("Query: ", buf, sizeof(buf), MUTT_NM_QUERY, false, NULL, NULL) != 0) ||
2365  (buf[0] == '\0'))
2366  {
2367  mutt_message(_("No query, aborting"));
2368  break;
2369  }
2370 
2371  // Keep copy of user's query to name the mailbox
2372  char *query_unencoded = mutt_str_dup(buf);
2373 
2374  struct Mailbox *m_query =
2375  change_folder_notmuch(menu, buf, sizeof(buf), &oldcount, &cur,
2376  (op == OP_MAIN_VFOLDER_FROM_QUERY_READONLY));
2377  if (m_query)
2378  {
2379  m_query->name = query_unencoded;
2380  query_unencoded = NULL;
2381  }
2382  else
2383  {
2384  FREE(&query_unencoded);
2385  }
2386 
2387  break;
2388  }
2389 
2390  case OP_MAIN_WINDOWED_VFOLDER_BACKWARD:
2391  {
2392  if (!prereq(Context, menu, CHECK_IN_MAILBOX))
2393  break;
2394  const short c_nm_query_window_duration =
2395  cs_subset_number(NeoMutt->sub, "nm_query_window_duration");
2396  if (c_nm_query_window_duration <= 0)
2397  {
2398  mutt_message(_("Windowed queries disabled"));
2399  break;
2400  }
2401  const char *c_nm_query_window_current_search =
2402  cs_subset_string(NeoMutt->sub, "nm_query_window_current_search");
2403  if (!c_nm_query_window_current_search)
2404  {
2405  mutt_message(_("No notmuch vfolder currently loaded"));
2406  break;
2407  }
2409  char buf[PATH_MAX] = { 0 };
2410  mutt_str_copy(buf, c_nm_query_window_current_search, sizeof(buf));
2411  change_folder_notmuch(menu, buf, sizeof(buf), &oldcount, &cur, false);
2412  break;
2413  }
2414 
2415  case OP_MAIN_WINDOWED_VFOLDER_FORWARD:
2416  {
2417  if (!prereq(Context, menu, CHECK_IN_MAILBOX))
2418  break;
2419  const short c_nm_query_window_duration =
2420  cs_subset_number(NeoMutt->sub, "nm_query_window_duration");
2421  if (c_nm_query_window_duration <= 0)
2422  {
2423  mutt_message(_("Windowed queries disabled"));
2424  break;
2425  }
2426  const char *c_nm_query_window_current_search =
2427  cs_subset_string(NeoMutt->sub, "nm_query_window_current_search");
2428  if (!c_nm_query_window_current_search)
2429  {
2430  mutt_message(_("No notmuch vfolder currently loaded"));
2431  break;
2432  }
2434  char buf[PATH_MAX] = { 0 };
2435  mutt_str_copy(buf, c_nm_query_window_current_search, sizeof(buf));
2436  change_folder_notmuch(menu, buf, sizeof(buf), &oldcount, &cur, false);
2437  break;
2438  }
2439 #endif
2440 
2441 #ifdef USE_SIDEBAR
2442  case OP_SIDEBAR_OPEN:
2443  {
2444  struct MuttWindow *win_sidebar = mutt_window_find(dlg, WT_SIDEBAR);
2445  change_folder_mailbox(menu, sb_get_highlight(win_sidebar), &oldcount, &cur, false);
2446  break;
2447  }
2448 #endif
2449 
2450  case OP_MAIN_NEXT_UNREAD_MAILBOX:
2451  {
2452  if (!prereq(Context, menu, CHECK_IN_MAILBOX))
2453  break;
2454 
2455  struct Mailbox *m = Context->mailbox;
2456 
2457  struct Buffer *folderbuf = mutt_buffer_pool_get();
2458  mutt_buffer_strcpy(folderbuf, mailbox_path(m));
2459  m = mutt_mailbox_next(m, folderbuf);
2460  mutt_buffer_pool_release(&folderbuf);
2461 
2462  if (!m)
2463  {
2464  mutt_error(_("No mailboxes have new mail"));
2465  break;
2466  }
2467 
2468  change_folder_mailbox(menu, m, &oldcount, &cur, false);
2469  break;
2470  }
2471 
2472  case OP_MAIN_CHANGE_FOLDER:
2473  case OP_MAIN_CHANGE_FOLDER_READONLY:
2474 #ifdef USE_NOTMUCH
2475  case OP_MAIN_CHANGE_VFOLDER: // now an alias for OP_MAIN_CHANGE_FOLDER
2476 #endif
2477  {
2478  bool pager_return = true; /* return to display message in pager */
2479  struct Buffer *folderbuf = mutt_buffer_pool_get();
2480  mutt_buffer_alloc(folderbuf, PATH_MAX);
2481 
2482  char *cp = NULL;
2483  bool read_only;
2484  const bool c_read_only = cs_subset_bool(NeoMutt->sub, "read_only");
2485  if (attach_msg || c_read_only || (op == OP_MAIN_CHANGE_FOLDER_READONLY))
2486  {
2487  cp = _("Open mailbox in read-only mode");
2488  read_only = true;
2489  }
2490  else
2491  {
2492  cp = _("Open mailbox");
2493  read_only = false;
2494  }
2495 
2496  const bool c_change_folder_next =
2497  cs_subset_bool(NeoMutt->sub, "change_folder_next");
2498  if (c_change_folder_next && ctx_mailbox(Context) &&
2500  {
2502  mutt_buffer_pretty_mailbox(folderbuf);
2503  }
2504  /* By default, fill buf with the next mailbox that contains unread mail */
2505  mutt_mailbox_next(Context ? Context->mailbox : NULL, folderbuf);
2506 
2507  if (mutt_buffer_enter_fname(cp, folderbuf, true, ctx_mailbox(Context),
2508  false, NULL, NULL, MUTT_SEL_NO_FLAGS) == -1)
2509  goto changefoldercleanup;
2510 
2511  /* Selected directory is okay, let's save it. */
2513 
2514  if (mutt_buffer_is_empty(folderbuf))
2515  {
2517  goto changefoldercleanup;
2518  }
2519 
2520  struct Mailbox *m = mx_mbox_find2(mutt_buffer_string(folderbuf));
2521  if (m)
2522  {
2523  change_folder_mailbox(menu, m, &oldcount, &cur, read_only);
2524  pager_return = false;
2525  }
2526  else
2527  {
2528  change_folder_string(menu, folderbuf->data, folderbuf->dsize,
2529  &oldcount, &cur, &pager_return, read_only);
2530  }
2531 
2532  changefoldercleanup:
2533  mutt_buffer_pool_release(&folderbuf);
2534  if (in_pager && pager_return)
2535  {
2536  op = OP_DISPLAY_MESSAGE;
2537  continue;
2538  }
2539  break;
2540  }
2541 
2542 #ifdef USE_NNTP
2543  case OP_MAIN_CHANGE_GROUP:
2544  case OP_MAIN_CHANGE_GROUP_READONLY:
2545  {
2546  bool pager_return = true; /* return to display message in pager */
2547  struct Buffer *folderbuf = mutt_buffer_pool_get();
2548  mutt_buffer_alloc(folderbuf, PATH_MAX);
2549 
2550  OptNews = false;
2551  bool read_only;
2552  char *cp = NULL;
2553  const bool c_read_only = cs_subset_bool(NeoMutt->sub, "read_only");
2554  if (attach_msg || c_read_only || (op == OP_MAIN_CHANGE_GROUP_READONLY))
2555  {
2556  cp = _("Open newsgroup in read-only mode");
2557  read_only = true;
2558  }
2559  else
2560  {
2561  cp = _("Open newsgroup");
2562  read_only = false;
2563  }
2564 
2565  const bool c_change_folder_next =
2566  cs_subset_bool(NeoMutt->sub, "change_folder_next");
2567  if (c_change_folder_next && ctx_mailbox(Context) &&
2569  {
2571  mutt_buffer_pretty_mailbox(folderbuf);
2572  }
2573 
2574  OptNews = true;
2575  const char *c_news_server =
2576  cs_subset_string(NeoMutt->sub, "news_server");
2578  c_news_server, false);
2579  if (!CurrentNewsSrv)
2580  goto changefoldercleanup2;
2581 
2582  nntp_mailbox(Context ? Context->mailbox : NULL, folderbuf->data,
2583  folderbuf->dsize);
2584 
2585  if (mutt_buffer_enter_fname(cp, folderbuf, true, ctx_mailbox(Context),
2586  false, NULL, NULL, MUTT_SEL_NO_FLAGS) == -1)
2587  goto changefoldercleanup2;
2588 
2589  /* Selected directory is okay, let's save it. */
2591 
2592  if (mutt_buffer_is_empty(folderbuf))
2593  {
2595  goto changefoldercleanup2;
2596  }
2597 
2598  struct Mailbox *m = mx_mbox_find2(mutt_buffer_string(folderbuf));
2599  if (m)
2600  {
2601  change_folder_mailbox(menu, m, &oldcount, &cur, read_only);
2602  pager_return = false;
2603  }
2604  else
2605  {
2606  change_folder_string(menu, folderbuf->data, folderbuf->dsize,
2607  &oldcount, &cur, &pager_return, read_only);
2608  }
2609  dlg->help_data = IndexNewsHelp;
2610 
2611  changefoldercleanup2:
2612  mutt_buffer_pool_release(&folderbuf);
2613  if (in_pager && pager_return)
2614  {
2615  op = OP_DISPLAY_MESSAGE;
2616  continue;
2617  }
2618  break;
2619  }
2620 #endif
2621 
2622  case OP_DISPLAY_MESSAGE:
2623  case OP_DISPLAY_HEADERS: /* don't weed the headers */
2624  {
2626  break;
2627  if (!cur.e)
2628  break;
2629  /* toggle the weeding of headers so that a user can press the key
2630  * again while reading the message. */
2631  if (op == OP_DISPLAY_HEADERS)
2632  bool_str_toggle(NeoMutt->sub, "weed", NULL);
2633 
2634  OptNeedResort = false;
2635 
2636  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
2637  if (((c_sort & SORT_MASK) == SORT_THREADS) && cur.e->collapsed)
2638  {
2641  const bool c_uncollapse_jump =
2642  cs_subset_bool(NeoMutt->sub, "uncollapse_jump");
2643  if (c_uncollapse_jump)
2644  menu->current = mutt_thread_next_unread(cur.e);
2645  }
2646 
2647  const bool c_pgp_auto_decode =
2648  cs_subset_bool(NeoMutt->sub, "pgp_auto_decode");
2649  if (c_pgp_auto_decode && (tag || !(cur.e->security & PGP_TRADITIONAL_CHECKED)))
2650  {
2651  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2652  el_add_tagged(&el, Context, cur.e, tag);
2654  emaillist_clear(&el);
2655  }
2657 
2658  op = mutt_display_message(win_index, win_ibar, win_pager, win_pbar,
2659  Context->mailbox, cur.e);
2660  window_set_focus(win_index);
2661  if (op < 0)
2662  {
2663  OptNeedResort = false;
2664  break;
2665  }
2666 
2667  /* This is used to redirect a single operation back here afterwards. If
2668  * mutt_display_message() returns 0, then this flag and pager state will
2669  * be cleaned up after this switch statement. */
2670  in_pager = true;
2671  menu->oldcurrent = menu->current;
2672  if (ctx_mailbox(Context))
2674  continue;
2675  }
2676 
2677  case OP_EXIT:
2678  close = op;
2679  if ((!in_pager) && attach_msg)
2680  {
2681  done = true;
2682  break;
2683  }
2684 
2685  const enum QuadOption c_quit = cs_subset_quad(NeoMutt->sub, "quit");
2686  if ((!in_pager) &&
2687  (query_quadoption(c_quit, _("Exit NeoMutt without saving?")) == MUTT_YES))
2688  {
2689  if (Context)
2690  {
2692  ctx_free(&Context);
2693  }
2694  done = true;
2695  }
2696  break;
2697 
2698  case OP_MAIN_BREAK_THREAD:
2699  {
2701  break;
2702  /* L10N: CHECK_ACL */
2703  if (!check_acl(Context->mailbox, MUTT_ACL_WRITE, _("Can't break thread")))
2704  break;
2705  if (!cur.e)
2706  break;
2707 
2708  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
2709  if ((c_sort & SORT_MASK) != SORT_THREADS)
2710  mutt_error(_("Threading is not enabled"));
2711  else if (!STAILQ_EMPTY(&cur.e->env->in_reply_to) ||
2712  !STAILQ_EMPTY(&cur.e->env->references))
2713  {
2714  {
2715  mutt_break_thread(cur.e);
2717  menu->current = cur.e->vnum;
2718  }
2719 
2720  Context->mailbox->changed = true;
2721  mutt_message(_("Thread broken"));
2722 
2723  if (in_pager)
2724  {
2725  op = OP_DISPLAY_MESSAGE;
2726  continue;
2727  }
2728  else
2729  menu->redraw |= REDRAW_INDEX;
2730  }
2731  else
2732  {
2733  mutt_error(
2734  _("Thread can't be broken, message is not part of a thread"));
2735  }
2736  break;
2737  }
2738 
2739  case OP_MAIN_LINK_THREADS:
2740  {
2742  break;
2743  /* L10N: CHECK_ACL */
2744  if (!check_acl(Context->mailbox, MUTT_ACL_WRITE, _("Can't link threads")))
2745  break;
2746  if (!cur.e)
2747  break;
2748 
2749  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
2750  if ((c_sort & SORT_MASK) != SORT_THREADS)
2751  mutt_error(_("Threading is not enabled"));
2752  else if (!cur.e->env->message_id)
2753  mutt_error(_("No Message-ID: header available to link thread"));
2754  else
2755  {
2756  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2757  el_add_tagged(&el, Context, NULL, true);
2758 
2759  if (mutt_link_threads(cur.e, &el, Context->mailbox))
2760  {
2762  menu->current = cur.e->vnum;
2763 
2764  Context->mailbox->changed = true;
2765  mutt_message(_("Threads linked"));
2766  }
2767  else
2768  mutt_error(_("No thread linked"));
2769 
2770  emaillist_clear(&el);
2771  }
2772 
2773  if (in_pager)
2774  {
2775  op = OP_DISPLAY_MESSAGE;
2776  continue;
2777  }
2778  else
2779  menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
2780 
2781  break;
2782  }
2783 
2784  case OP_EDIT_TYPE:
2785  {
2787  break;
2788  if (!cur.e)
2789  break;
2790  mutt_edit_content_type(cur.e, cur.e->body, NULL);
2791  /* if we were in the pager, redisplay the message */
2792  if (in_pager)
2793  {
2794  op = OP_DISPLAY_MESSAGE;
2795  continue;
2796  }
2797  else
2798  menu->redraw = REDRAW_CURRENT;
2799  break;
2800  }
2801 
2802  case OP_MAIN_NEXT_UNDELETED:
2804  break;
2805  if (menu->current >= (Context->mailbox->vcount - 1))
2806  {
2807  if (!in_pager)
2808  mutt_message(_("You are on the last message"));
2809  break;
2810  }
2811  menu->current = ci_next_undeleted(Context->mailbox, menu->current);
2812  if (menu->current == -1)
2813  {
2814  menu->current = menu->oldcurrent;
2815  if (!in_pager)
2816  mutt_error(_("No undeleted messages"));
2817  }
2818  else if (in_pager)
2819  {
2820  op = OP_DISPLAY_MESSAGE;
2821  continue;
2822  }
2823  else
2824  menu->redraw = REDRAW_MOTION;
2825  break;
2826 
2827  case OP_NEXT_ENTRY:
2829  break;
2830  if (menu->current >= (Context->mailbox->vcount - 1))
2831  {
2832  if (!in_pager)
2833  mutt_message(_("You are on the last message"));
2834  break;
2835  }
2836  menu->current++;
2837  if (in_pager)
2838  {
2839  op = OP_DISPLAY_MESSAGE;
2840  continue;
2841  }
2842  else
2843  menu->redraw = REDRAW_MOTION;
2844  break;
2845 
2846  case OP_MAIN_PREV_UNDELETED:
2848  break;
2849  if (menu->current < 1)
2850  {
2851  mutt_message(_("You are on the first message"));
2852  break;
2853  }
2855  if (menu->current == -1)
2856  {
2857  menu->current = menu->oldcurrent;
2858  if (!in_pager)
2859  mutt_error(_("No undeleted messages"));
2860  }
2861  else if (in_pager)
2862  {
2863  op = OP_DISPLAY_MESSAGE;
2864  continue;
2865  }
2866  else
2867  menu->redraw = REDRAW_MOTION;
2868  break;
2869 
2870  case OP_PREV_ENTRY:
2872  break;
2873  if (menu->current < 1)
2874  {
2875  if (!in_pager)
2876  mutt_message(_("You are on the first message"));
2877  break;
2878  }
2879  menu->current--;
2880  if (in_pager)
2881  {
2882  op = OP_DISPLAY_MESSAGE;
2883  continue;
2884  }
2885  else
2886  menu->redraw = REDRAW_MOTION;
2887  break;
2888 
2889  case OP_DECRYPT_COPY:
2890  case OP_DECRYPT_SAVE:
2891  if (!WithCrypto)
2892  break;
2893  /* fallthrough */
2894  case OP_COPY_MESSAGE:
2895  case OP_SAVE:
2896  case OP_DECODE_COPY:
2897  case OP_DECODE_SAVE:
2898  {
2900  break;
2901  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2902  el_add_tagged(&el, Context, cur.e, tag);
2903 
2904  const enum MessageSaveOpt save_opt =
2905  ((op == OP_SAVE) || (op == OP_DECODE_SAVE) || (op == OP_DECRYPT_SAVE)) ?
2906  SAVE_MOVE :
2907  SAVE_COPY;
2908 
2909  enum MessageTransformOpt transform_opt =
2910  ((op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY)) ? TRANSFORM_DECODE :
2911  ((op == OP_DECRYPT_SAVE) || (op == OP_DECRYPT_COPY)) ? TRANSFORM_DECRYPT :
2913 
2914  const int rc = mutt_save_message(Context->mailbox, &el, save_opt, transform_opt);
2915  if ((rc == 0) && (save_opt == SAVE_MOVE))
2916  {
2917  menu->redraw |= REDRAW_STATUS;
2918  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
2919  if (tag)
2920  menu->redraw |= REDRAW_INDEX;
2921  else if (c_resolve)
2922  {
2923  menu->current = ci_next_undeleted(Context->mailbox, menu->current);
2924  if (menu->current == -1)
2925  {
2926  menu->current = menu->oldcurrent;
2927  menu->redraw |= REDRAW_CURRENT;
2928  }
2929  else
2930  menu->redraw |= REDRAW_MOTION_RESYNC;
2931  }
2932  else
2933  menu->redraw |= REDRAW_CURRENT;
2934  }
2935  emaillist_clear(&el);
2936  break;
2937  }
2938 
2939  case OP_MAIN_NEXT_NEW:
2940  case OP_MAIN_NEXT_UNREAD:
2941  case OP_MAIN_PREV_NEW:
2942  case OP_MAIN_PREV_UNREAD:
2943  case OP_MAIN_NEXT_NEW_THEN_UNREAD:
2944  case OP_MAIN_PREV_NEW_THEN_UNREAD:
2945  {
2947  break;
2948 
2949  int first_unread = -1;
2950  int first_new = -1;
2951 
2952  const int saved_current = menu->current;
2953  int mcur = menu->current;
2954  menu->current = -1;
2955  for (size_t i = 0; i != Context->mailbox->vcount; i++)
2956  {
2957  if ((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_NEXT_UNREAD) ||
2958  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD))
2959  {
2960  mcur++;
2961  if (mcur > (Context->mailbox->vcount - 1))
2962  {
2963  mcur = 0;
2964  }
2965  }
2966  else
2967  {
2968  mcur--;
2969  if (mcur < 0)
2970  {
2971  mcur = Context->mailbox->vcount - 1;
2972  }
2973  }
2974 
2975  struct Email *e = mutt_get_virt_email(Context->mailbox, mcur);
2976  if (!e)
2977  break;
2978  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
2979  if (e->collapsed && ((c_sort & SORT_MASK) == SORT_THREADS))
2980  {
2981  int unread = mutt_thread_contains_unread(e);
2982  if ((unread != 0) && (first_unread == -1))
2983  first_unread = mcur;
2984  if ((unread == 1) && (first_new == -1))
2985  first_new = mcur;
2986  }
2987  else if (!e->deleted && !e->read)
2988  {
2989  if (first_unread == -1)
2990  first_unread = mcur;
2991  if (!e->old && (first_new == -1))
2992  first_new = mcur;
2993  }
2994 
2995  if (((op == OP_MAIN_NEXT_UNREAD) || (op == OP_MAIN_PREV_UNREAD)) &&
2996  (first_unread != -1))
2997  break;
2998  if (((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_PREV_NEW) ||
2999  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD) || (op == OP_MAIN_PREV_NEW_THEN_UNREAD)) &&
3000  (first_new != -1))
3001  {
3002  break;
3003  }
3004  }
3005  if (((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_PREV_NEW) ||
3006  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD) || (op == OP_MAIN_PREV_NEW_THEN_UNREAD)) &&
3007  (first_new != -1))
3008  {
3009  menu->current = first_new;
3010  }
3011  else if (((op == OP_MAIN_NEXT_UNREAD) || (op == OP_MAIN_PREV_UNREAD) ||
3012  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD) || (op == OP_MAIN_PREV_NEW_THEN_UNREAD)) &&
3013  (first_unread != -1))
3014  {
3015  menu->current = first_unread;
3016  }
3017 
3018  if (menu->current == -1)
3019  {
3020  menu->current = menu->oldcurrent;
3021  if ((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_PREV_NEW))
3022  {
3023  if (ctx_has_limit(Context))
3024  mutt_error(_("No new messages in this limited view"));
3025  else
3026  mutt_error(_("No new messages"));
3027  }
3028  else
3029  {
3030  if (ctx_has_limit(Context))
3031  mutt_error(_("No unread messages in this limited view"));
3032  else
3033  mutt_error(_("No unread messages"));
3034  }
3035  break;
3036  }
3037 
3038  if ((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_NEXT_UNREAD) ||
3039  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD))
3040  {
3041  if (saved_current > menu->current)
3042  {
3043  mutt_message(_("Search wrapped to top"));
3044  }
3045  }
3046  else if (saved_current < menu->current)
3047  {
3048  mutt_message(_("Search wrapped to bottom"));
3049  }
3050 
3051  if (in_pager)
3052  {
3053  op = OP_DISPLAY_MESSAGE;
3054  continue;
3055  }
3056  else
3057  menu->redraw = REDRAW_MOTION;
3058  break;
3059  }
3060  case OP_FLAG_MESSAGE:
3061  {
3063  break;
3064  /* L10N: CHECK_ACL */
3065  if (!check_acl(Context->mailbox, MUTT_ACL_WRITE, _("Can't flag message")))
3066  break;
3067 
3068  struct Mailbox *m = Context->mailbox;
3069  if (tag)
3070  {
3071  for (size_t i = 0; i < m->msg_count; i++)
3072  {
3073  struct Email *e = m->emails[i];
3074  if (!e)
3075  break;
3076  if (message_is_tagged(Context, e))
3077  mutt_set_flag(m, e, MUTT_FLAG, !e->flagged);
3078  }
3079 
3080  menu->redraw |= REDRAW_INDEX;
3081  }
3082  else
3083  {
3084  if (!cur.e)
3085  break;
3086  mutt_set_flag(m, cur.e, MUTT_FLAG, !cur.e->flagged);
3087  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3088  if (c_resolve)
3089  {
3090  menu->current = ci_next_undeleted(Context->mailbox, menu->current);
3091  if (menu->current == -1)
3092  {
3093  menu->current = menu->oldcurrent;
3094  menu->redraw |= REDRAW_CURRENT;
3095  }
3096  else
3097  menu->redraw |= REDRAW_MOTION_RESYNC;
3098  }
3099  else
3100  menu->redraw |= REDRAW_CURRENT;
3101  }
3102  menu->redraw |= REDRAW_STATUS;
3103  break;
3104  }
3105 
3106  case OP_TOGGLE_NEW:
3107  {
3109  break;
3110  /* L10N: CHECK_ACL */
3111  if (!check_acl(Context->mailbox, MUTT_ACL_SEEN, _("Can't toggle new")))
3112  break;
3113 
3114  struct Mailbox *m = Context->mailbox;
3115  if (tag)
3116  {
3117  for (size_t i = 0; i < m->msg_count; i++)
3118  {
3119  struct Email *e = m->emails[i];
3120  if (!e)
3121  break;
3122  if (!message_is_tagged(Context, e))
3123  continue;
3124 
3125  if (e->read || e->old)
3126  mutt_set_flag(m, e, MUTT_NEW, true);
3127  else
3128  mutt_set_flag(m, e, MUTT_READ, true);
3129  }
3130  menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3131  }
3132  else
3133  {
3134  if (!cur.e)
3135  break;
3136  if (cur.e->read || cur.e->old)
3137  mutt_set_flag(m, cur.e, MUTT_NEW, true);
3138  else
3139  mutt_set_flag(m, cur.e, MUTT_READ, true);
3140 
3141  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3142  if (c_resolve)
3143  {
3144  menu->current = ci_next_undeleted(Context->mailbox, menu->current);
3145  if (menu->current == -1)
3146  {
3147  menu->current = menu->oldcurrent;
3148  menu->redraw |= REDRAW_CURRENT;
3149  }
3150  else
3151  menu->redraw |= REDRAW_MOTION_RESYNC;
3152  }
3153  else
3154  menu->redraw |= REDRAW_CURRENT;
3155  menu->redraw |= REDRAW_STATUS;
3156  }
3157  break;
3158  }
3159 
3160  case OP_TOGGLE_WRITE:
3161  if (!prereq(Context, menu, CHECK_IN_MAILBOX))
3162  break;
3163  if (mx_toggle_write(Context->mailbox) == 0)
3164  {
3165  if (in_pager)
3166  {
3167  op = OP_DISPLAY_MESSAGE;
3168  continue;
3169  }
3170  else
3171  menu->redraw |= REDRAW_STATUS;
3172  }
3173  break;
3174 
3175  case OP_MAIN_NEXT_THREAD:
3176  case OP_MAIN_NEXT_SUBTHREAD:
3177  case OP_MAIN_PREV_THREAD:
3178  case OP_MAIN_PREV_SUBTHREAD:
3179  {
3181  break;
3182 
3183  switch (op)
3184  {
3185  case OP_MAIN_NEXT_THREAD:
3186  menu->current = mutt_next_thread(cur.e);
3187  break;
3188 
3189  case OP_MAIN_NEXT_SUBTHREAD:
3190  menu->current = mutt_next_subthread(cur.e);
3191  break;
3192 
3193  case OP_MAIN_PREV_THREAD:
3194  menu->current = mutt_previous_thread(cur.e);
3195  break;
3196 
3197  case OP_MAIN_PREV_SUBTHREAD:
3198  menu->current = mutt_previous_subthread(cur.e);
3199  break;
3200  }
3201 
3202  if (menu->current < 0)
3203  {
3204  menu->current = menu->oldcurrent;
3205  if ((op == OP_MAIN_NEXT_THREAD) || (op == OP_MAIN_NEXT_SUBTHREAD))
3206  mutt_error(_("No more threads"));
3207  else
3208  mutt_error(_("You are on the first thread"));
3209  }
3210  else if (in_pager)
3211  {
3212  op = OP_DISPLAY_MESSAGE;
3213  continue;
3214  }
3215  else
3216  menu->redraw = REDRAW_MOTION;
3217  break;
3218  }
3219 
3220  case OP_MAIN_ROOT_MESSAGE:
3221  case OP_MAIN_PARENT_MESSAGE:
3222  {
3224  break;
3225 
3226  menu->current = mutt_parent_message(cur.e, op == OP_MAIN_ROOT_MESSAGE);
3227  if (menu->current < 0)
3228  {
3229  menu->current = menu->oldcurrent;
3230  }
3231  else if (in_pager)
3232  {
3233  op = OP_DISPLAY_MESSAGE;
3234  continue;
3235  }
3236  else
3237  menu->redraw = REDRAW_MOTION;
3238  break;
3239  }
3240 
3241  case OP_MAIN_SET_FLAG:
3242  case OP_MAIN_CLEAR_FLAG:
3243  {
3245  break;
3246  /* check_acl(MUTT_ACL_WRITE); */
3247 
3248  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3249  el_add_tagged(&el, Context, cur.e, tag);
3250 
3251  if (mutt_change_flag(Context->mailbox, &el, (op == OP_MAIN_SET_FLAG)) == 0)
3252  {
3253  menu->redraw |= REDRAW_STATUS;
3254  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3255  if (tag)
3256  menu->redraw |= REDRAW_INDEX;
3257  else if (c_resolve)
3258  {
3259  menu->current = ci_next_undeleted(Context->mailbox, menu->current);
3260  if (menu->current == -1)
3261  {
3262  menu->current = menu->oldcurrent;
3263  menu->redraw |= REDRAW_CURRENT;
3264  }
3265  else
3266  menu->redraw |= REDRAW_MOTION_RESYNC;
3267  }
3268  else
3269  menu->redraw |= REDRAW_CURRENT;
3270  }
3271  emaillist_clear(&el);
3272  break;
3273  }
3274 
3275  case OP_MAIN_COLLAPSE_THREAD:
3276  {
3278  break;
3279 
3280  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
3281  if ((c_sort & SORT_MASK) != SORT_THREADS)
3282  {
3283  mutt_error(_("Threading is not enabled"));
3284  break;
3285  }
3286 
3287  if (!cur.e)
3288  break;
3289 
3290  if (cur.e->collapsed)
3291  {
3292  menu->current = mutt_uncollapse_thread(cur.e);
3294  const bool c_uncollapse_jump =
3295  cs_subset_bool(NeoMutt->sub, "uncollapse_jump");
3296  if (c_uncollapse_jump)
3297  menu->current = mutt_thread_next_unread(cur.e);
3298  }
3299  else if (mutt_thread_can_collapse(cur.e))
3300  {
3301  menu->current = mutt_collapse_thread(cur.e);
3303  }
3304  else
3305  {
3306  mutt_error(_("Thread contains unread or flagged messages"));
3307  break;
3308  }
3309 
3310  menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
3311 
3312  break;
3313  }
3314 
3315  case OP_MAIN_COLLAPSE_ALL:
3316  {
3317  if (!prereq(Context, menu, CHECK_IN_MAILBOX))
3318  break;
3319 
3320  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
3321  if ((c_sort & SORT_MASK) != SORT_THREADS)
3322  {
3323  mutt_error(_("Threading is not enabled"));
3324  break;
3325  }
3326  collapse_all(Context, menu, 1);
3327  break;
3328  }
3329 
3330  /* --------------------------------------------------------------------
3331  * These functions are invoked directly from the internal-pager
3332  */
3333 
3334  case OP_BOUNCE_MESSAGE:
3335  {
3337  break;
3338  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3339  el_add_tagged(&el, Context, cur.e, tag);
3341  emaillist_clear(&el);
3342  break;
3343  }
3344 
3345  case OP_CREATE_ALIAS:
3346  {
3347  struct AddressList *al = NULL;
3348  if (cur.e && cur.e->env)
3349  al = mutt_get_address(cur.e->env, NULL);
3350  alias_create(al, NeoMutt->sub);
3351  menu->redraw |= REDRAW_CURRENT;
3352  break;
3353  }
3354 
3355  case OP_QUERY:
3356  if (!prereq(Context, menu, CHECK_ATTACH))
3357  break;
3359  break;
3360 
3361  case OP_PURGE_MESSAGE:
3362  case OP_DELETE:
3363  {
3365  break;
3366  /* L10N: CHECK_ACL */
3367  if (!check_acl(Context->mailbox, MUTT_ACL_DELETE, _("Can't delete message")))
3368  break;
3369 
3370  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3371  el_add_tagged(&el, Context, cur.e, tag);
3372 
3374  mutt_emails_set_flag(Context->mailbox, &el, MUTT_PURGE, (op == OP_PURGE_MESSAGE));
3375  const bool c_delete_untag =
3376  cs_subset_bool(NeoMutt->sub, "delete_untag");
3377  if (c_delete_untag)
3379  emaillist_clear(&el);
3380 
3381  if (tag)
3382  {
3383  menu->redraw |= REDRAW_INDEX;
3384  }
3385  else
3386  {
3387  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3388  if (c_resolve)
3389  {
3390  menu->current = ci_next_undeleted(Context->mailbox, menu->current);
3391  if (menu->current == -1)
3392  {
3393  menu->current = menu->oldcurrent;
3394  menu->redraw |= REDRAW_CURRENT;
3395  }
3396  else if (in_pager)
3397  {
3398  op = OP_DISPLAY_MESSAGE;
3399  continue;
3400  }
3401  else
3402  menu->redraw |= REDRAW_MOTION_RESYNC;
3403  }
3404  else
3405  menu->redraw |= REDRAW_CURRENT;
3406  }
3407  menu->redraw |= REDRAW_STATUS;
3408  break;
3409  }
3410 
3411  case OP_DELETE_THREAD:
3412  case OP_DELETE_SUBTHREAD:
3413  case OP_PURGE_THREAD:
3414  {
3416  break;
3417  /* L10N: CHECK_ACL */
3418  /* L10N: Due to the implementation details we do not know whether we
3419  delete zero, 1, 12, ... messages. So in English we use
3420  "messages". Your language might have other means to express this. */
3421  if (!check_acl(Context->mailbox, MUTT_ACL_DELETE, _("Can't delete messages")))
3422  break;
3423  if (!cur.e)
3424  break;
3425 
3426  int subthread = (op == OP_DELETE_SUBTHREAD);
3428  true, subthread);
3429  if (rc == -1)
3430  break;
3431  if (op == OP_PURGE_THREAD)
3432  {
3433  rc = mutt_thread_set_flag(ctx_mailbox(Context), cur.e, MUTT_PURGE, true, subthread);
3434  if (rc == -1)
3435  break;
3436  }
3437 
3438  const bool c_delete_untag =
3439  cs_subset_bool(NeoMutt->sub, "delete_untag");
3440  if (c_delete_untag)
3441  mutt_thread_set_flag(ctx_mailbox(Context), cur.e, MUTT_TAG, false, subthread);
3442  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3443  if (c_resolve)
3444  {
3445  menu->current = ci_next_undeleted(Context->mailbox, menu->current);
3446  if (menu->current == -1)
3447  menu->current = menu->oldcurrent;
3448  }
3449  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
3450  break;
3451  }
3452 
3453 #ifdef USE_NNTP
3454  case OP_CATCHUP:
3456  break;
3457  if (Context && (Context->mailbox->type == MUTT_NNTP))
3458  {
3459  struct NntpMboxData *mdata = Context->mailbox->mdata;
3460  if (mutt_newsgroup_catchup(Context->mailbox, mdata->adata, mdata->group))
3461  menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
3462  }
3463  break;
3464 #endif
3465 
3466  case OP_DISPLAY_ADDRESS:
3467  {
3469  break;
3470  if (!cur.e)
3471  break;
3472  mutt_display_address(cur.e->env);
3473  break;
3474  }
3475 
3476  case OP_ENTER_COMMAND:
3478  window_set_focus(win_index);
3479  if (Context)
3481  menu->redraw = REDRAW_FULL;
3482  break;
3483 
3484  case OP_EDIT_OR_VIEW_RAW_MESSAGE:
3485  case OP_EDIT_RAW_MESSAGE:
3486  case OP_VIEW_RAW_MESSAGE:
3487  {
3488  /* TODO split this into 3 cases? */
3490  break;
3491  bool edit;
3492  if (op == OP_EDIT_RAW_MESSAGE)
3493  {
3494  if (!prereq(Context, menu, CHECK_READONLY))
3495  break;
3496  /* L10N: CHECK_ACL */
3497  if (!check_acl(Context->mailbox, MUTT_ACL_INSERT, _("Can't edit message")))
3498  break;
3499  edit = true;
3500  }
3501  else if (op == OP_EDIT_OR_VIEW_RAW_MESSAGE)
3503  else
3504  edit = false;
3505 
3506  if (!cur.e)
3507  break;
3508  const bool c_pgp_auto_decode =
3509  cs_subset_bool(NeoMutt->sub, "pgp_auto_decode");
3510  if (c_pgp_auto_decode && (tag || !(cur.e->security & PGP_TRADITIONAL_CHECKED)))
3511  {
3512  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3513  el_add_tagged(&el, Context, cur.e, tag);
3515  emaillist_clear(&el);
3516  }
3517  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3518  el_add_tagged(&el, Context, cur.e, tag);
3519  mutt_ev_message(Context->mailbox, &el, edit ? EVM_EDIT : EVM_VIEW);
3520  emaillist_clear(&el);
3521  menu->redraw = REDRAW_FULL;
3522 
3523  break;
3524  }
3525 
3526  case OP_FORWARD_MESSAGE:
3527  {
3529  break;
3530  if (!cur.e)
3531  break;
3532  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3533  el_add_tagged(&el, Context, cur.e, tag);
3534  const bool c_pgp_auto_decode =
3535  cs_subset_bool(NeoMutt->sub, "pgp_auto_decode");
3536  if (c_pgp_auto_decode && (tag || !(cur.e->security & PGP_TRADITIONAL_CHECKED)))
3537  {
3539  }
3540  mutt_send_message(SEND_FORWARD, NULL, NULL, Context, &el, NeoMutt->sub);
3541  emaillist_clear(&el);
3542  menu->redraw = REDRAW_FULL;
3543  break;
3544  }
3545 
3546  case OP_FORGET_PASSPHRASE:
3548  break;
3549 
3550  case OP_GROUP_REPLY:
3551  case OP_GROUP_CHAT_REPLY:
3552  {
3553  SendFlags replyflags = SEND_REPLY;
3554  if (op == OP_GROUP_REPLY)
3555  replyflags |= SEND_GROUP_REPLY;
3556  else
3557  replyflags |= SEND_GROUP_CHAT_REPLY;
3559  break;
3560  if (!cur.e)
3561  break;
3562  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3563  el_add_tagged(&el, Context, cur.e, tag);
3564  const bool c_pgp_auto_decode =
3565  cs_subset_bool(NeoMutt->sub, "pgp_auto_decode");
3566  if (c_pgp_auto_decode && (tag || !(cur.e->security & PGP_TRADITIONAL_CHECKED)))
3567  {
3569  }
3570  mutt_send_message(replyflags, NULL, NULL, Context, &el, NeoMutt->sub);
3571  emaillist_clear(&el);
3572  menu->redraw = REDRAW_FULL;
3573  break;
3574  }
3575 
3576  case OP_EDIT_LABEL:
3577  {
3579  break;
3580 
3581  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3582  el_add_tagged(&el, Context, cur.e, tag);
3583  int num_changed = mutt_label_message(Context->mailbox, &el);
3584  emaillist_clear(&el);
3585 
3586  if (num_changed > 0)
3587  {
3588  Context->mailbox->changed = true;
3589  menu->redraw = REDRAW_FULL;
3590  /* L10N: This is displayed when the x-label on one or more
3591  messages is edited. */
3592  mutt_message(ngettext("%d label changed", "%d labels changed", num_changed),
3593  num_changed);
3594  }
3595  else
3596  {
3597  /* L10N: This is displayed when editing an x-label, but no messages
3598  were updated. Possibly due to canceling at the prompt or if the new
3599  label is the same as the old label. */
3600  mutt_message(_("No labels changed"));
3601  }
3602  break;
3603  }
3604 
3605  case OP_LIST_REPLY:
3606  {
3608  break;
3609  if (!cur.e)
3610  break;
3611  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3612  el_add_tagged(&el, Context, cur.e, tag);
3613  const bool c_pgp_auto_decode =
3614  cs_subset_bool(NeoMutt->sub, "pgp_auto_decode");
3615  if (c_pgp_auto_decode && (tag || !(cur.e->security & PGP_TRADITIONAL_CHECKED)))
3616  {
3618  }
3620  &el, NeoMutt->sub);
3621  emaillist_clear(&el);
3622  menu->redraw = REDRAW_FULL;
3623  break;
3624  }
3625 
3626  case OP_MAIL:
3627  if (!prereq(Context, menu, CHECK_ATTACH))
3628  break;
3629  mutt_send_message(SEND_NO_FLAGS, NULL, NULL, Context, NULL, NeoMutt->sub);
3630  menu->redraw = REDRAW_FULL;
3631  break;
3632 
3633  case OP_MAIL_KEY:
3634  if (!(WithCrypto & APPLICATION_PGP))
3635  break;
3636  if (!prereq(Context, menu, CHECK_ATTACH))
3637  break;
3638  mutt_send_message(SEND_KEY, NULL, NULL, NULL, NULL, NeoMutt->sub);
3639  menu->redraw = REDRAW_FULL;
3640  break;
3641 
3642  case OP_EXTRACT_KEYS:
3643  {
3644  if (!WithCrypto)
3645  break;
3647  break;
3648  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3649  el_add_tagged(&el, Context, cur.e, tag);
3651  emaillist_clear(&el);
3652  menu->redraw = REDRAW_FULL;
3653  break;
3654  }
3655 
3656  case OP_CHECK_TRADITIONAL:
3657  {
3658  if (!(WithCrypto & APPLICATION_PGP))
3659  break;
3661  break;
3662  if (!cur.e)
3663  break;
3664  if (tag || !(cur.e->security & PGP_TRADITIONAL_CHECKED))
3665  {
3666  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3667  el_add_tagged(&el, Context, cur.e, tag);
3669  emaillist_clear(&el);
3670  }
3671 
3672  if (in_pager)
3673  {
3674  op = OP_DISPLAY_MESSAGE;
3675  continue;
3676  }
3677  break;
3678  }
3679 
3680  case OP_PIPE:
3681  {
3683  break;
3684  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3685  el_add_tagged(&el, Context, cur.e, tag);
3687  emaillist_clear(&el);
3688 
3689 #ifdef USE_IMAP
3690  /* in an IMAP folder index with imap_peek=no, piping could change
3691  * new or old messages status to read. Redraw what's needed. */
3692  const bool c_imap_peek = cs_subset_bool(NeoMutt->sub, "imap_peek");
3693  if ((Context->mailbox->type == MUTT_IMAP) && !c_imap_peek)
3694  {
3695  menu->redraw |= (tag ? REDRAW_INDEX : REDRAW_CURRENT) | REDRAW_STATUS;
3696  }
3697 #endif
3698  break;
3699  }
3700 
3701  case OP_PRINT:
3702  {
3704  break;
3705  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3706  el_add_tagged(&el, Context, cur.e, tag);
3708  emaillist_clear(&el);
3709 
3710 #ifdef USE_IMAP
3711  /* in an IMAP folder index with imap_peek=no, printing could change
3712  * new or old messages status to read. Redraw what's needed. */
3713  const bool c_imap_peek = cs_subset_bool(NeoMutt->sub, "imap_peek");
3714  if ((Context->mailbox->type == MUTT_IMAP) && !c_imap_peek)
3715  {
3716  menu->redraw |= (tag ? REDRAW_INDEX : REDRAW_CURRENT) | REDRAW_STATUS;
3717  }
3718 #endif
3719  break;
3720  }
3721 
3722  case OP_MAIN_READ_THREAD:
3723  case OP_MAIN_READ_SUBTHREAD:
3724  {
3726  break;
3727  /* L10N: CHECK_ACL */
3728  /* L10N: Due to the implementation details we do not know whether we
3729  mark zero, 1, 12, ... messages as read. So in English we use
3730  "messages". Your language might have other means to express this. */
3731  if (!check_acl(Context->mailbox, MUTT_ACL_SEEN, _("Can't mark messages as read")))
3732  break;
3733 
3735  true, (op != OP_MAIN_READ_THREAD));
3736  if (rc != -1)
3737  {
3738  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3739  if (c_resolve)
3740  {
3741  menu->current = ((op == OP_MAIN_READ_THREAD) ? mutt_next_thread(cur.e) :
3742  mutt_next_subthread(cur.e));
3743  if (menu->current == -1)
3744  {
3745  menu->current = menu->oldcurrent;
3746  }
3747  else if (in_pager)
3748  {
3749  op = OP_DISPLAY_MESSAGE;
3750  continue;
3751  }
3752  }
3753  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
3754  }
3755  break;
3756  }
3757 
3758  case OP_MARK_MSG:
3759  {
3761  break;
3762  if (!cur.e)
3763  break;
3764  if (cur.e->env->message_id)
3765  {
3766  char buf2[128];
3767 
3768  buf2[0] = '\0';
3769  /* L10N: This is the prompt for <mark-message>. Whatever they
3770  enter will be prefixed by $mark_macro_prefix and will become
3771  a macro hotkey to jump to the currently selected message. */
3772  if (!mutt_get_field(_("Enter macro stroke: "), buf2, sizeof(buf2),
3773  MUTT_COMP_NO_FLAGS, false, NULL, NULL) &&
3774  buf2[0])
3775  {
3776  char str[256], macro[256];
3777  const char *c_mark_macro_prefix =
3778  cs_subset_string(NeoMutt->sub, "mark_macro_prefix");
3779  snprintf(str, sizeof(str), "%s%s", c_mark_macro_prefix, buf2);
3780  snprintf(macro, sizeof(macro), "<search>~i \"%s\"\n", cur.e->env->message_id);
3781  /* L10N: "message hotkey" is the key bindings menu description of a
3782  macro created by <mark-message>. */
3783  km_bind(str, MENU_MAIN, OP_MACRO, macro, _("message hotkey"));
3784 
3785  /* L10N: This is echoed after <mark-message> creates a new hotkey
3786  macro. %s is the hotkey string ($mark_macro_prefix followed
3787  by whatever they typed at the prompt.) */
3788  snprintf(buf2, sizeof(buf2), _("Message bound to %s"), str);
3789  mutt_message(buf2);
3790  mutt_debug(LL_DEBUG1, "Mark: %s => %s\n", str, macro);
3791  }
3792  }
3793  else
3794  {
3795  /* L10N: This error is printed if <mark-message> can't find a
3796  Message-ID for the currently selected message in the index. */
3797  mutt_error(_("No message ID to macro"));
3798  }
3799  break;
3800  }
3801 
3802  case OP_RECALL_MESSAGE:
3803  if (!prereq(Context, menu, CHECK_ATTACH))
3804  break;
3805  mutt_send_message(SEND_POSTPONED, NULL, NULL, Context, NULL, NeoMutt->sub);
3806  menu->redraw = REDRAW_FULL;
3807  break;
3808 
3809  case OP_RESEND:
3811  break;
3812 
3813  if (tag)
3814  {
3815  struct Mailbox *m = Context->mailbox;
3816  for (size_t i = 0; i < m->msg_count; i++)
3817  {
3818  struct Email *e = m->emails[i];
3819  if (!e)
3820  break;
3821  if (message_is_tagged(Context, e))
3822  mutt_resend_message(NULL, Context, e, NeoMutt->sub);
3823  }
3824  }
3825  else
3826  {
3827  mutt_resend_message(NULL, Context, cur.e, NeoMutt->sub);
3828  }
3829 
3830  menu->redraw = REDRAW_FULL;
3831  break;
3832 
3833 #ifdef USE_NNTP
3834  case OP_FOLLOWUP:
3835  case OP_FORWARD_TO_GROUP:
3837  break;
3838  /* fallthrough */
3839 
3840  case OP_POST:
3841  {
3843  break;
3844  if (!cur.e)
3845  break;
3846  const enum QuadOption c_followup_to_poster =
3847  cs_subset_quad(NeoMutt->sub, "followup_to_poster");
3848  if ((op != OP_FOLLOWUP) || !cur.e->env->followup_to ||
3849  !mutt_istr_equal(cur.e->env->followup_to, "poster") ||
3850  (query_quadoption(c_followup_to_poster,
3851  _("Reply by mail as poster prefers?")) != MUTT_YES))
3852  {
3853  const enum QuadOption c_post_moderated =
3854  cs_subset_quad(NeoMutt->sub, "post_moderated");
3855  if (Context && (Context->mailbox->type == MUTT_NNTP) &&
3856  !((struct NntpMboxData *) Context->mailbox->mdata)->allowed && (query_quadoption(c_post_moderated, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES))
3857  {
3858  break;
3859  }
3860  if (op == OP_POST)
3861  mutt_send_message(SEND_NEWS, NULL, NULL, Context, NULL, NeoMutt->sub);
3862  else
3863  {
3865  break;
3866  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3867  el_add_tagged(&el, Context, cur.e, tag);
3868  mutt_send_message(((op == OP_FOLLOWUP) ? SEND_REPLY : SEND_FORWARD) | SEND_NEWS,
3869  NULL, NULL, Context, &el, NeoMutt->sub);
3870  emaillist_clear(&el);
3871  }
3872  menu->redraw = REDRAW_FULL;
3873  break;
3874  }
3875  }
3876 #endif
3877  /* fallthrough */
3878  case OP_REPLY:
3879  {
3881  break;
3882  if (!cur.e)
3883  break;
3884  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3885  el_add_tagged(&el, Context, cur.e, tag);
3886  const bool c_pgp_auto_decode =
3887  cs_subset_bool(NeoMutt->sub, "pgp_auto_decode");
3888  if (c_pgp_auto_decode && (tag || !(cur.e->security & PGP_TRADITIONAL_CHECKED)))
3889  {
3891  }
3892  mutt_send_message(SEND_REPLY, NULL, NULL, Context, &el, NeoMutt->sub);
3893  emaillist_clear(&el);
3894  menu->redraw = REDRAW_FULL;
3895  break;
3896  }
3897 
3898  case OP_SHELL_ESCAPE:
3899  if (mutt_shell_escape())
3900  {
3902  }
3903  break;
3904 
3905  case OP_TAG_THREAD:
3906  case OP_TAG_SUBTHREAD:
3907  {
3909  break;
3910  if (!cur.e)
3911  break;
3912 
3914  !cur.e->tagged, (op != OP_TAG_THREAD));
3915  if (rc != -1)
3916  {
3917  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3918  if (c_resolve)
3919  {
3920  if (op == OP_TAG_THREAD)
3921  menu->current = mutt_next_thread(cur.e);
3922  else
3923  menu->current = mutt_next_subthread(cur.e);
3924 
3925  if (menu->current == -1)
3926  menu->current = menu->oldcurrent;
3927  }
3928  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
3929  }
3930  break;
3931  }
3932 
3933  case OP_UNDELETE:
3934  {
3936  break;
3937  /* L10N: CHECK_ACL */
3938  if (!check_acl(Context->mailbox, MUTT_ACL_DELETE, _("Can't undelete message")))
3939  break;
3940 
3941  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3942  el_add_tagged(&el, Context, cur.e, tag);
3943 
3946  emaillist_clear(&el);
3947 
3948  if (tag)
3949  {
3950  menu->redraw |= REDRAW_INDEX;
3951  }
3952  else
3953  {
3954  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3955  if (c_resolve && (menu->current < (Context->mailbox->vcount - 1)))
3956  {
3957  menu->current++;
3958  menu->redraw |= REDRAW_MOTION_RESYNC;
3959  }
3960  else
3961  menu->redraw |= REDRAW_CURRENT;
3962  }
3963 
3964  menu->redraw |= REDRAW_STATUS;
3965  break;
3966  }
3967 
3968  case OP_UNDELETE_THREAD:
3969  case OP_UNDELETE_SUBTHREAD:
3970  {
3972  break;
3973  /* L10N: CHECK_ACL */
3974  /* L10N: Due to the implementation details we do not know whether we
3975  undelete zero, 1, 12, ... messages. So in English we use
3976  "messages". Your language might have other means to express this. */
3977  if (!check_acl(Context->mailbox, MUTT_ACL_DELETE, _("Can't undelete messages")))
3978  break;
3979 
3981  false, (op != OP_UNDELETE_THREAD));
3982  if (rc != -1)
3983  {
3985  false, (op != OP_UNDELETE_THREAD));
3986  }
3987  if (rc != -1)
3988  {
3989  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3990  if (c_resolve)
3991  {
3992  if (op == OP_UNDELETE_THREAD)
3993  menu->current = mutt_next_thread(cur.e);
3994  else
3995  menu->current = mutt_next_subthread(cur.e);
3996 
3997  if (menu->current == -1)
3998  menu->current = menu->oldcurrent;
3999  }
4000  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
4001  }
4002  break;
4003  }
4004 
4005  case OP_VERSION:
4007  break;
4008 
4009  case OP_MAILBOX_LIST:
4011  break;
4012 
4013  case OP_VIEW_ATTACHMENTS:
4015  break;
4016  if (!cur.e)
4017  break;
4018  dlg_select_attachment(cur.e);
4019  if (cur.e->attach_del)
4020  Context->mailbox->changed = true;
4021  menu->redraw = REDRAW_FULL;
4022  break;
4023 
4024  case OP_END_COND:
4025  break;
4026 
4027  case OP_WHAT_KEY:
4028  mutt_what_key();
4029  break;
4030 
4031 #ifdef USE_SIDEBAR
4032  case OP_SIDEBAR_FIRST:
4033  case OP_SIDEBAR_LAST:
4034  case OP_SIDEBAR_NEXT:
4035  case OP_SIDEBAR_NEXT_NEW:
4036  case OP_SIDEBAR_PAGE_DOWN:
4037  case OP_SIDEBAR_PAGE_UP:
4038  case OP_SIDEBAR_PREV:
4039  case OP_SIDEBAR_PREV_NEW:
4040  {
4041  struct MuttWindow *win_sidebar = mutt_window_find(dlg, WT_SIDEBAR);
4042  if (!win_sidebar)
4043  break;
4044  sb_change_mailbox(win_sidebar, op);
4045  break;
4046  }
4047 
4048  case OP_SIDEBAR_TOGGLE_VISIBLE:
4049  bool_str_toggle(NeoMutt->sub, "sidebar_visible", NULL);
4050  mutt_window_reflow(NULL);
4051  break;
4052 #endif
4053 
4054 #ifdef USE_AUTOCRYPT
4055  case OP_AUTOCRYPT_ACCT_MENU:
4057  break;
4058 #endif
4059 
4060  default:
4061  if (!in_pager)
4063  }
4064 
4065 #ifdef USE_NOTMUCH
4066  if (Context)
4068 #endif
4069 
4070  if (in_pager)
4071  {
4073  in_pager = false;
4074  menu->redraw = REDRAW_FULL;
4075  }
4076 
4077  if (done)
4078  break;
4079  }
4080 
4081  mutt_menu_pop_current(menu);
4082  mutt_menu_free(&menu);
4083  return close;
4084 }
4085 
4091 void mutt_set_header_color(struct Mailbox *m, struct Email *e)
4092 {
4093  if (!e)
4094  return;
4095 
4096  struct ColorLine *color = NULL;
4097  struct PatternCache cache = { 0 };
4098 
4099  STAILQ_FOREACH(color, &Colors->index_list, entries)
4100  {
4102  MUTT_MATCH_FULL_ADDRESS, m, e, &cache))
4103  {
4104  e->pair = color->pair;
4105  return;
4106  }
4107  }
4109 }
4110 
4117 static struct MuttWindow *create_panel_index(struct MuttWindow *parent, bool status_on_top)
4118 {
4119  struct MuttWindow *panel_index =
4122  parent->focus = panel_index;
4123 
4124  struct MuttWindow *win_index =
4127  panel_index->focus = win_index;
4128 
4129  struct MuttWindow *win_ibar =
4132 
4133  if (status_on_top)
4134  {
4135  mutt_window_add_child(panel_index, win_ibar);
4136  mutt_window_add_child(panel_index, win_index);
4137  }
4138  else
4139  {
4140  mutt_window_add_child(panel_index, win_index);
4141  mutt_window_add_child(panel_index, win_ibar);
4142  }
4143 
4144  return panel_index;
4145 }
4146 
4153 static struct MuttWindow *create_panel_pager(struct MuttWindow *parent, bool status_on_top)
4154 {
4155  struct MuttWindow *panel_pager =
4158  panel_pager->state.visible = false; // The Pager and Pager Bar are initially hidden
4159 
4160  struct MuttWindow *win_pager =
4163  panel_pager->focus = win_pager;
4164 
4165  struct MuttWindow *win_pbar =
4168 
4169  if (status_on_top)
4170  {
4171  mutt_window_add_child(panel_pager, win_pbar);
4172  mutt_window_add_child(panel_pager, win_pager);
4173  }
4174  else
4175  {
4176  mutt_window_add_child(panel_pager, win_pager);
4177  mutt_window_add_child(panel_pager, win_pbar);
4178  }
4179 
4180  return panel_pager;
4181 }
4182 
4188 {
4189  struct MuttWindow *dlg =
4192 
4193  const bool c_status_on_top = cs_subset_bool(NeoMutt->sub, "status_on_top");
4194  mutt_window_add_child(dlg, create_panel_index(dlg, c_status_on_top));
4195  mutt_window_add_child(dlg, create_panel_pager(dlg, c_status_on_top));
4196 
4197  index_add_observers(dlg);
4198  return dlg;
4199 }
4200 
4206 {
4208 }
Menu::oldcurrent
int oldcurrent
For driver use only.
Definition: mutt_menu.h:76
Body::hdr_offset
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:42
ctx_free
void ctx_free(struct Context **ptr)
Free a Context.
Definition: context.c:50
lib.h
mutt_search_command
int mutt_search_command(struct Context *ctx, struct Mailbox *m, int cur, int op)
Perform a search.
Definition: pattern.c:493
mutt_get_virt_email
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:397
mutt_curses_set_cursor
void mutt_curses_set_cursor(enum MuttCursorState state)
Set the cursor state.
Definition: mutt_curses.c:78
SendFlags
uint16_t SendFlags
Flags for mutt_send_message(), e.g. SEND_REPLY.
Definition: send.h:37
Email::msgno
int msgno
Number displayed to the user.
Definition: email.h:87
SEND_GROUP_CHAT_REPLY
#define SEND_GROUP_CHAT_REPLY
Reply to all recipients preserving To/Cc.
Definition: send.h:53
dump_graphviz
void dump_graphviz(const char *title, struct Context *ctx)
Definition: graphviz.c:937
IndexHelp
static const struct Mapping IndexHelp[]
Help Bar for the Index dialog.
Definition: index.c:105
mutt_beep
void mutt_beep(bool force)
Irritate the user.
Definition: curs_lib.c:99
el_add_tagged
int el_add_tagged(struct EmailList *el, struct Context *ctx, struct Email *e, bool use_tagged)
Get a list of the tagged Emails.
Definition: context.c:349
lib.h
WT_PAGER
@ WT_PAGER
Window containing paged free-form text.
Definition: mutt_window.h:97
mx_mbox_check
enum MxStatus mx_mbox_check(struct Mailbox *m)
Check for new mail - Wrapper for MxOps::mbox_check()
Definition: mx.c:1137
menu_middle_page
void menu_middle_page(struct Menu *menu)
Move the focus to the centre of the page.
Definition: menu.c:794
mutt_window_clrtoeol
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:244
Mailbox::rights
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:121
lib.h
ctx_has_limit
bool ctx_has_limit(const struct Context *ctx)
Is a limit active?
Definition: context.c:418
MUTT_FLAG
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:102
MUTT_READONLY
#define MUTT_READONLY
Open in read-only mode.
Definition: mx.h:54
mx_path_canon
int mx_path_canon(char *buf, size_t buflen, const char *folder, enum MailboxType *type)
Canonicalise a mailbox path - Wrapper for MxOps::path_canon()
Definition: mx.c:1381
nntp_check_msgid
int nntp_check_msgid(struct Mailbox *m, const char *msgid)
Fetch article by Message-ID.
Definition: nntp.c:2118
QuadOption
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
SEND_REPLY
#define SEND_REPLY
Reply to sender.
Definition: send.h:41
NotifyCallback
Data passed to a notification function.
Definition: observer.h:39
WT_PAGER_BAR
@ WT_PAGER_BAR
Pager Bar containing status info about the Pager.
Definition: mutt_window.h:98
Envelope::followup_to
char * followup_to
List of 'followup-to' fields.
Definition: envelope.h:77
mutt_window_move
int mutt_window_move(struct MuttWindow *win, int col, int row)
Move the cursor in a Window.
Definition: mutt_window.c:382
index_make_entry
void index_make_entry(struct Menu *menu, char *buf, size_t buflen, int line)
Format a menu item for the index list - Implements Menu::make_entry()
Definition: index.c:866
SigWinch
WHERE SIG_ATOMIC_VOLATILE_T SigWinch
true after SIGWINCH is received
Definition: mutt_globals.h:75
MxStatus
MxStatus
Return values from mx_mbox_check(), mx_mbox_sync(), and mx_mbox_close()
Definition: mx.h:71
ColorLine::regex
regex_t regex
Compiled regex.
Definition: color.h:37
mutt_ev_message
int mutt_ev_message(struct Mailbox *m, struct EmailList *el, enum EvMessage action)
Edit or view a message.
Definition: editmsg.c:280
MessageWindow
struct MuttWindow * MessageWindow
Message Window, ":set", etc.
Definition: mutt_window.c:47
mutt_timeout_hook
void mutt_timeout_hook(void)
Execute any timeout hooks.
Definition: hook.c:801
_
#define _(a)
Definition: message.h:28
NONULL
#define NONULL(x)
Definition: string2.h:37
Mailbox
A mailbox.
Definition: mailbox.h:81
SORT_THREADS
@ SORT_THREADS
Sort by email threads.
Definition: sort2.h:51
TRANSFORM_NONE
@ TRANSFORM_NONE
No transformation.
Definition: commands.h:51
mutt_check_traditional_pgp
bool mutt_check_traditional_pgp(struct Mailbox *m, struct EmailList *el, MuttRedrawFlags *redraw)
Check if a message has inline PGP content.
Definition: commands.c:1467
MUTT_WIN_ORIENT_HORIZONTAL
@ MUTT_WIN_ORIENT_HORIZONTAL
Window uses all available horizontal space.
Definition: mutt_window.h:36
Envelope::in_reply_to
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:82
Envelope::message_id
char * message_id
Message ID.
Definition: envelope.h:69
mutt_make_string
void mutt_make_string(char *buf, size_t buflen, int cols, const char *s, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition: hdrline.c:1427
OptNeedResort
WHERE bool OptNeedResort
(pseudo) used to force a re-sort
Definition: options.h:43
Mailbox::v2r
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:101
Mailbox::emails
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
mutt_strn_equal
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:593
CHECK_VISIBLE
#define CHECK_VISIBLE
Is the selected message visible in the index?
Definition: index.c:144
mutt_system
int mutt_system(const char *cmd)
Run an external command.
Definition: system.c:51
mutt_mailbox_list
bool mutt_mailbox_list(void)
List the mailboxes with new mail.
Definition: mutt_mailbox.c:220
nntp_expand_path
void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *acct)
Make fully qualified url from newsgroup name.
Definition: newsrc.c:560
MUTT_FORMAT_FORCESUBJ
#define MUTT_FORMAT_FORCESUBJ
Print the subject even if unchanged.
Definition: format_flags.h:31
MT_COLOR_STATUS
@ MT_COLOR_STATUS
Status bar (takes a pattern)
Definition: color.h:94
ListNode
A List node for strings.
Definition: list.h:34
Buffer
String manipulation buffer.
Definition: buffer.h:33
mutt_monitor_remove
int mutt_monitor_remove(struct Mailbox *m)
Remove a watch for a mailbox.
Definition: monitor.c:526
menu_redraw_index
void menu_redraw_index(struct Menu *menu)
Force the redraw of the index.
Definition: menu.c:381
Email::thread
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
nm_query_window_forward
void nm_query_window_forward(void)
Function to move the current search window forward in time.
Definition: notmuch.c:1738
log_queue_save
int log_queue_save(FILE *fp)
Save the contents of the queue to a temporary file.
Definition: logging.c:369
mutt_show_error
void mutt_show_error(void)
Show the user an error message.
Definition: curs_lib.c:558
Connection::account
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:36
Body::offset
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
mx_toggle_write
static int mx_toggle_write(struct Mailbox *m)
Toggle the mailbox's readonly flag.
Definition: index.c:383
ctx_mailbox
struct Mailbox * ctx_mailbox(struct Context *ctx)
wrapper to get the mailbox in a Context, or NULL
Definition: context.c:429
MUTT_ACL_INSERT
#define MUTT_ACL_INSERT
Add/copy into the mailbox (used when editing a message)
Definition: mailbox.h:69
SAVE_COPY
@ SAVE_COPY
Copy message, making a duplicate in another mailbox.
Definition: commands.h:61
mutt_window_addnstr
int mutt_window_addnstr(const char *str, int num)
Write a partial string to a Window.
Definition: mutt_window.c:504
mutt_parent_message
int mutt_parent_message(struct Email *e, bool find_root)
Find the parent of a message.
Definition: mutt_thread.c:1182
window_set_focus
void window_set_focus(struct MuttWindow *win)
Set the Window focus.
Definition: mutt_window.c:763
mutt_file_fclose
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
mutt_buffer_pretty_mailbox
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:599
mutt_next_thread
#define mutt_next_thread(e)
Definition: mutt_thread.h:96
mutt_buffer_is_empty
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
MUTT_ACL_DELETE
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:66
index_pager_init
struct MuttWindow * index_pager_init(void)
Allocate the Windows for the Index/Pager.
Definition: index.c:4187
format_flags.h
nm_url_from_query
char * nm_url_from_query(struct Mailbox *m, char *buf, size_t buflen)
Turn a query into a URL.
Definition: notmuch.c:1677
Body
The body of an email.
Definition: body.h:34
MUTT_FORMAT_ARROWCURSOR
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
Mailbox::notify
struct Notify * notify
Notifications handler.
Definition: mailbox.h:144
CHECK_MSGCOUNT
#define CHECK_MSGCOUNT
Are there any messages?
Definition: index.c:143
SEND_KEY
#define SEND_KEY
Mail a PGP public key.
Definition: send.h:47
change_folder_mailbox
static void change_folder_mailbox(struct Menu *menu, struct Mailbox *m, int *oldcount, const struct CurrentEmail *cur, bool read_only)
Change to a different Mailbox by pointer.
Definition: index.c:689
NT_GLOBAL
@ NT_GLOBAL
Not object-related, NotifyGlobal.
Definition: notify_type.h:40
mutt_window_clearline
void mutt_window_clearline(struct MuttWindow *win, int row)
Clear a row of a Window.
Definition: mutt_window.c:232
mutt_pattern_func
int mutt_pattern_func(struct Context *ctx, int op, char *prompt)
Perform some Pattern matching.
Definition: pattern.c:344
dialog_find
struct MuttWindow * dialog_find(struct MuttWindow *win)
Find the parent Dialog of a Window.
Definition: dialog.c:46
MUTT_SEL_NO_FLAGS
#define MUTT_SEL_NO_FLAGS
No flags are set.
Definition: browser.h:41
CHECK_READONLY
#define CHECK_READONLY
Is the mailbox readonly?
Definition: index.c:145
MUTT_PROGRESS_WRITE
@ MUTT_PROGRESS_WRITE
Progress tracks elements, according to $write_inc
Definition: progress.h:43
index_color
int index_color(struct Menu *menu, int line)
Calculate the colour for a line of the index - Implements Menu::color()
Definition: index.c:945
NeoMutt::notify
struct Notify * notify
Notifications handler.
Definition: neomutt.h:38
Context::vsize
off_t vsize
Size (in bytes) of the messages shown.
Definition: context.h:40
message_is_tagged
bool message_is_tagged(struct Context *ctx, struct Email *e)
Is a message in the index tagged (and within limit)
Definition: context.c:335
mutt_pattern_exec
int mutt_pattern_exec(struct Pattern *pat, PatternExecFlags flags, struct Mailbox *m, struct Email *e, struct PatternCache *cache)
Match a pattern against an email header.
Definition: exec.c:738
MuttWindow
A division of the screen.
Definition: mutt_window.h:115
mutt_folder_hook
void mutt_folder_hook(const char *path, const char *desc)
Perform a folder hook.
Definition: hook.c:505
EVM_VIEW
@ EVM_VIEW
View the message.
Definition: protos.h:56
cs_subset_bool
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:69
monitor.h
Email::offset
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:84
nntp_mailbox
void nntp_mailbox(struct Mailbox *m, char *buf, size_t buflen)
Get first newsgroup with new messages.
Definition: newsrc.c:1379
menu_redraw_current
void menu_redraw_current(struct Menu *menu)
Redraw the current menu.
Definition: menu.c:494
MUTT_YES
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:40
mutt_check_rescore
void mutt_check_rescore(struct Mailbox *m)
Do the emails need to have their scores recalculated?
Definition: score.c:71
mutt_messages_in_thread
int mutt_messages_in_thread(struct Mailbox *m, struct Email *e, enum MessageInThread mit)
Count the messages in a thread.
Definition: mutt_thread.c:1476
Context
The "current" mailbox.
Definition: context.h:38
mutt_str_dup
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
mutt_globals.h
mutt_file_fopen
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
LL_DEBUG1
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
Mailbox::flags
uint8_t flags
e.g. MB_NORMAL
Definition: mailbox.h:134
mutt_strwidth
int mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:1363
MUTT_FORMAT_TREE
#define MUTT_FORMAT_TREE
Draw the thread tree.
Definition: format_flags.h:32
SAVE_MOVE
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition: commands.h:62
FREE
#define FREE(x)
Definition: memory.h:40
options.h
WindowState::visible
bool visible
Window is visible.
Definition: mutt_window.h:56
SORT_REVERSE
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:81
STAILQ_FIRST
#define STAILQ_FIRST(head)
Definition: queue.h:347
mutt_perror
#define mutt_perror(...)
Definition: logging.h:85
mutt_draw_statusline
void mutt_draw_statusline(int cols, const char *buf, size_t buflen)
Draw a highlighted status bar.
Definition: index.c:974
mutt_shell_escape
bool mutt_shell_escape(void)
invoke a command in a subshell
Definition: commands.c:845
RootWindow
struct MuttWindow * RootWindow
Parent of all Windows.
Definition: mutt_window.c:45
mutt_update_index
void mutt_update_index(struct Menu *menu, struct Context *ctx, enum MxStatus check, int oldcount, const struct Email *cur_email)
Update the index.
Definition: index.c:654
mutt_print_message
void mutt_print_message(struct Mailbox *m, struct EmailList *el)
Print a message.
Definition: commands.c:730
lib.h
sb_get_highlight
struct Mailbox * sb_get_highlight(struct MuttWindow *win)
Get the Mailbox that's highlighted in the sidebar.
Definition: sidebar.c:56
mutt_menu_new
struct Menu * mutt_menu_new(enum MenuType type)
Create a new Menu.
Definition: menu.c:956
MUTT_MAILBOX_ERROR
@ MUTT_MAILBOX_ERROR
Error occurred examining Mailbox.
Definition: mailbox.h:46
mutt_draw_tree
void mutt_draw_tree(struct ThreadsContext *tctx)
Draw a tree of threaded emails.
Definition: mutt_thread.c:320
Buffer::dsize
size_t dsize
Length of data.
Definition: buffer.h:37
menu_bottom_page
void menu_bottom_page(struct Menu *menu)
Move the focus to the bottom of the page.
Definition: menu.c:776
MUTT_MATCH_FULL_ADDRESS
#define MUTT_MATCH_FULL_ADDRESS
Match the full address.
Definition: lib.h:99
SORT_MASK
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:80
mutt_label_message
int mutt_label_message(struct Mailbox *m, struct EmailList *el)
Let the user label a message.
Definition: mutt_header.c:124
MuttWindow::help_menu
int help_menu
Menu for key bindings, e.g. MENU_PAGER.
Definition: mutt_window.h:134
LastKey
int LastKey
contains the last key the user pressed
Definition: keymap.c:146
REDRAW_MOTION_RESYNC
#define REDRAW_MOTION_RESYNC
Redraw any changing the menu selection.
Definition: mutt_menu.h:42
lib.h
PATH_MAX
#define PATH_MAX
Definition: mutt.h:44
is_current_email
static bool is_current_email(const struct CurrentEmail *cur, const struct Email *e)
Check whether an email is the currently selected Email.
Definition: index.c:584
ci_bounce_message
void ci_bounce_message(struct Mailbox *m, struct EmailList *el)
Bounce an email.
Definition: commands.c:411
update_index_threaded
static void update_index_threaded(struct Context *ctx, enum MxStatus check, int oldcount)
Update the index (if threaded)
Definition: index.c:452
menu_next_line
void menu_next_line(struct Menu *menu)
Move the view down one line, keeping the selection the same.
Definition: menu.c:625
Menu::make_entry
void(* make_entry)(struct Menu *menu, char *buf, size_t buflen, int line)
Format a item for a menu.
Definition: mutt_menu.h:88
mutt_mailbox_next
struct Mailbox * mutt_mailbox_next(struct Mailbox *m_cur, struct Buffer *s)
incoming folders completion routine
Definition: mutt_mailbox.c:312
mutt_change_flag
int mutt_change_flag(struct Mailbox *m, struct EmailList *el, bool bf)
Change the flag on a Message.
Definition: flags.c:436
mutt_buffer_pool_release
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
MUTT_LIMIT
@ MUTT_LIMIT
Messages in limited view.
Definition: mutt.h:105
STAILQ_EMPTY
#define STAILQ_EMPTY(head)
Definition: queue.h:345
mx_path_probe
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1335
mx_tags_commit
int mx_tags_commit(struct Mailbox *m, struct Email *e, char *tags)
Save tags to the Mailbox - Wrapper for MxOps::tags_commit()
Definition: mx.c:1308
MUTT_WIN_SIZE_MAXIMISE
@ MUTT_WIN_SIZE_MAXIMISE
Window wants as much space as possible.
Definition: mutt_window.h:45
mutt_header.h
AclFlags
uint16_t AclFlags
ACL Rights - These show permission to...
Definition: mailbox.h:62
Menu::win_ibar
struct MuttWindow * win_ibar
Definition: mutt_menu.h:64
mx_path_resolve
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1681
mutt_str_atoi
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:252
STAILQ_FOREACH
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
mutt_set_vnum
off_t mutt_set_vnum(struct Mailbox *m)
Set the virtual index number of all the messages in a mailbox.
Definition: mutt_thread.c:1232
Mailbox::vcount
int vcount
The number of virtual messages.
Definition: mailbox.h:102
dlg_select_autocrypt_account
void dlg_select_autocrypt_account(struct Mailbox *m)
Display the Autocrypt account Menu.
Definition: dlgautocrypt.c:257
MT_COLOR_NORMAL
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:77
MX_STATUS_REOPENED
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mx.h:77
mutt_buffer_alloc
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:265
Email::sequence
size_t sequence
Sequence number assigned on creation.
Definition: email.h:99
notify_send
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:152
WT_SIDEBAR
@ WT_SIDEBAR
Side panel containing Accounts or groups of data.
Definition: mutt_window.h:99
NntpAccountData::conn
struct Connection * conn
Definition: adata.h:60
TRANSFORM_DECODE
@ TRANSFORM_DECODE
Decode message.
Definition: commands.h:53
mutt_monitor_add
int mutt_monitor_add(struct Mailbox *m)
Add a watch for a mailbox.
Definition: monitor.c:481
mutt_thread_can_collapse
bool mutt_thread_can_collapse(struct Email *e)
Check whether a thread can be collapsed.
Definition: mutt_thread.c:1632
Email::tree
char * tree
Character string to print thread tree.
Definition: email.h:94
MuttThread::prev
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
Context::msg_in_pager
int msg_in_pager
Message currently shown in the pager.
Definition: context.h:44
Context::threads
struct ThreadsContext * threads
Threads context.
Definition: context.h:43
MUTT_READ
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:96
mutt_istr_equal
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:883
mutt_hash_find
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:354
mutt_mailbox_notify
bool mutt_mailbox_notify(struct Mailbox *m_cur)
Notify the user if there's new mail.
Definition: mutt_mailbox.c:207
MuttThread::parent
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
menu_current_top
void menu_current_top(struct Menu *menu)
Move the current selection to the top of the window.
Definition: menu.c:845
Mapping
Mapping between user-readable string and a constant.
Definition: mapping.h:31
mutt_ts_icon
void mutt_ts_icon(char *str)
Set the icon in the terminal title bar.
Definition: terminal.c:118
MX_STATUS_NEW_MAIL
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mx.h:75
EventMailbox
An Event that happened to a Mailbox.
Definition: mailbox.h:183
lib.h
alias_create
void alias_create(struct AddressList *al, const struct ConfigSubset *sub)
Create a new Alias from an Address.
Definition: alias.c:366
mx_mbox_sync
enum MxStatus mx_mbox_sync(struct Mailbox *m)
Save changes to mailbox.
Definition: mx.c:937
mutt_save_message
int mutt_save_message(struct Mailbox *m, struct EmailList *el, enum MessageSaveOpt save_opt, enum MessageTransformOpt transform_opt)
Save an email.
Definition: commands.c:1037
PGP_TRADITIONAL_CHECKED
#define PGP_TRADITIONAL_CHECKED
Email has a traditional (inline) signature.
Definition: lib.h:99
mutt_sort_headers
void mutt_sort_headers(struct Mailbox *m, struct ThreadsContext *threads, bool init, off_t *vsize)
Sort emails by their headers.
Definition: sort.c:367
SEND_NEWS
#define SEND_NEWS
Reply to a news article.
Definition: send.h:54
Mailbox::dontwrite
bool dontwrite
Don't write the mailbox on close.
Definition: mailbox.h:115
Email::old
bool old
Email is seen, but unread.
Definition: email.h:50
protos.h
mutt_progress_init
void mutt_progress_init(struct Progress *progress, const char *msg, enum ProgressType type, size_t size)
Set up a progress bar.
Definition: progress.c:153
MUTT_PURGE
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:100
NT_MAILBOX
@ NT_MAILBOX
Mailbox has changed, NotifyMailbox, EventMailbox.
Definition: notify_type.h:42
NT_GLOBAL_SHUTDOWN
@ NT_GLOBAL_SHUTDOWN
NeoMutt is about to close.
Definition: neomutt.h:51
MIT_POSITION
@ MIT_POSITION
Definition: mutt_thread.h:85
nntp_check_children
int nntp_check_children(struct Mailbox *m, const char *msgid)
Fetch children of article with the Message-ID.
Definition: nntp.c:2188
mutt_resend_message
int mutt_resend_message(FILE *fp, struct Context *ctx, struct Email *e_cur, struct ConfigSubset *sub)
Resend an email.
Definition: send.c:1521
query_quadoption
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: curs_lib.c:518
mutt_menu_push_current
void mutt_menu_push_current(struct Menu *menu)
Add a new Menu to the stack.
Definition: menu.c:1015
mutt_previous_subthread
#define mutt_previous_subthread(e)
Definition: mutt_thread.h:99
SEND_FORWARD
#define SEND_FORWARD
Forward email.
Definition: send.h:44
mutt_buffer_pool_get
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
menu_prev_line
void menu_prev_line(struct Menu *menu)
Move the view up one line, keeping the selection the same.
Definition: menu.c:652
MuttWindow::focus
struct MuttWindow * focus
Focussed Window.
Definition: mutt_window.h:133
MUTT_NNTP
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition: mailbox.h:52
sb_change_mailbox
void sb_change_mailbox(struct MuttWindow *win, int op)
Perform a Sidebar function.
Definition: functions.c:265
mutt_ts_status
void mutt_ts_status(char *str)
Set the text of the terminal title bar.
Definition: terminal.c:104
Mailbox::type
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
menu_check_recenter
void menu_check_recenter(struct Menu *menu)
Recentre the menu on screen.
Definition: menu.c:544
Mailbox::mdata
void * mdata
Driver specific data.
Definition: mailbox.h:136
mutt_paddstr
void mutt_paddstr(int n, const char *s)
Display a string on screen, padded if necessary.
Definition: curs_lib.c:1270
MuttWindow::notify
struct Notify * notify
Notifications system.
Definition: mutt_window.h:131
mutt_edit_content_type
bool mutt_edit_content_type(struct Email *e, struct Body *b, FILE *fp)
Edit the content type of an attachment.
Definition: commands.c:1324
cs_subset_number
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:111
set_current_email
static void set_current_email(struct CurrentEmail *cur, struct Email *e)
Keep track of the currently selected Email.
Definition: index.c:594
CurrentEmail::e
struct Email * e
Current email.
Definition: index.c:573
SEND_POSTPONED
#define SEND_POSTPONED
Recall a postponed email.
Definition: send.h:45
mutt_help
void mutt_help(enum MenuType menu)
Display the help menu.
Definition: help.c:385
SEND_TO_SENDER
#define SEND_TO_SENDER
Compose new email to sender.
Definition: send.h:52
Email::visible
bool visible
Is this message part of the view?
Definition: email.h:74
MUTT_WIN_ORIENT_VERTICAL
@ MUTT_WIN_ORIENT_VERTICAL
Window uses all available vertical space.
Definition: mutt_window.h:35
mutt_do_pager
int mutt_do_pager(const char *banner, const char *tempfile, PagerFlags do_color, struct Pager *info)
Display some page-able text to the user.
Definition: curs_lib.c:691
MUTT_NEW
@ MUTT_NEW
New messages.
Definition: mutt.h:93
keymap.h
mx_tags_edit
int mx_tags_edit(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
start the tag editor of the mailbox
Definition: mx.c:1288
lib.h
muttlib.h
MUTT_UNDELETE
@ MUTT_UNDELETE
Messages to be un-deleted.
Definition: mutt.h:99
Email::tagged
bool tagged
Email is tagged.
Definition: email.h:44
mutt_limit_current_thread
bool mutt_limit_current_thread(struct Context *ctx, struct Email *e)
Limit the email view to the current thread.
Definition: pattern.c:190
km_bind
enum CommandResult km_bind(char *s, enum MenuType menu, int op, char *macro, char *desc)
Bind a key to a macro.
Definition: keymap.c:478
mx_mbox_close
enum MxStatus mx_mbox_close(struct Context **ptr)
Save changes and close mailbox.
Definition: mx.c:632
Progress
A progress bar.
Definition: progress.h:50
notify_observer_remove
bool notify_observer_remove(struct Notify *notify, observer_t callback, void *global_data)
Remove an observer from an object.
Definition: notify.c:207
OptAttachMsg
WHERE bool OptAttachMsg
(pseudo) used by attach-message
Definition: options.h:31
Body::length
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
Mailbox::changed
bool changed
Mailbox has been modified.
Definition: mailbox.h:114
Mailbox::name
char * name
A short name for the Mailbox.
Definition: mailbox.h:85
OpStrings
const char * OpStrings[][2]
Definition: opcodes.c:28
APPLICATION_PGP
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:97
mutt_menu.h
REDRAW_FULL
#define REDRAW_FULL
Redraw everything.
Definition: mutt_menu.h:45
MessageSaveOpt
MessageSaveOpt
Message save option.
Definition: commands.h:59
imap_logout_all
void imap_logout_all(void)
close all open connections
Definition: imap.c:560
window_redraw
void window_redraw(struct MuttWindow *win, bool force)
Reflow, recalc and repaint a tree of Windows.
Definition: mutt_window.c:747
lib.h
MuttThread::message
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
TRANSFORM_DECRYPT
@ TRANSFORM_DECRYPT
Decrypt message.
Definition: commands.h:52
mutt_thread_set_flag
int mutt_thread_set_flag(struct Mailbox *m, struct Email *e, enum MessageType flag, bool bf, bool subthread)
Set a flag on an entire thread.
Definition: flags.c:377
Mailbox::msg_count
int msg_count
Total number of messages.
Definition: mailbox.h:91
mutt_mailbox_check
int mutt_mailbox_check(struct Mailbox *m_cur, int force)
Check all all Mailboxes for new mail.
Definition: mutt_mailbox.c:141
LastFolder
WHERE char * LastFolder
Previously selected mailbox.
Definition: mutt_globals.h:55
mutt_clear_error
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
CHECK_IN_MAILBOX
#define CHECK_IN_MAILBOX
Is there a mailbox open?
Definition: index.c:142
MUTT_UNKNOWN
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition: mailbox.h:47
mutt_resize_screen
void mutt_resize_screen(void)
Update NeoMutt's opinion about the window size (CURSES)
Definition: resize.c:101
MAX
#define MAX(a, b)
Definition: memory.h:30
mdata.h
mutt_startup_shutdown_hook
void mutt_startup_shutdown_hook(HookFlags type)
Execute any startup/shutdown hooks.
Definition: hook.c:837
mutt_send_message
int mutt_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile, struct Context *ctx, struct EmailList *el, struct ConfigSubset *sub)
Send an email.
Definition: send.c:2012
mutt_window_find
struct MuttWindow * mutt_window_find(struct MuttWindow *root, enum WindowType type)
Find a Window of a given type.
Definition: mutt_window.c:678
mutt_window_new
struct MuttWindow * mutt_window_new(enum WindowType type, enum MuttWindowOrientation orient, enum MuttWindowSize size, int cols, int rows)
Create a new Window.
Definition: mutt_window.c:166
NotifyCallback::global_data
void * global_data
Data from notify_observer_add()
Definition: observer.h:45
dlg_select_attachment
void dlg_select_attachment(struct Email *e)
Show the attachments in a Menu.
Definition: recvattach.c:1549
nm_db_longrun_init
void nm_db_longrun_init(struct Mailbox *m, bool writable)
Start a long transaction.
Definition: db.c:291
Menu::pagelen
int pagelen
Number of entries per screen.
Definition: mutt_menu.h:60
MUTT_FORMAT_INDEX
#define MUTT_FORMAT_INDEX
This is a main index entry.
Definition: format_flags.h:36
CurrentEmail
Keep track of the currently selected Email.
Definition: index.c:571
NT_MAILBOX_SWITCH
@ NT_MAILBOX_SWITCH
Current Mailbox has changed.
Definition: mailbox.h:175
nm_query_window_backward
void nm_query_window_backward(void)
Function to move the current search window backward in time.
Definition: notmuch.c:1754
MUTT_NOTMUCH
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:54
MUTT_IMAP
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:53
collapse_all
static void collapse_all(struct Context *ctx, struct Menu *menu, int toggle)
Collapse/uncollapse all threads.
Definition: index.c:233
menu_last_entry
void menu_last_entry(struct Menu *menu)
Move the focus to the last entry in the menu.
Definition: menu.c:829
mutt_display_message
int mutt_display_message(struct MuttWindow *win_index, struct MuttWindow *win_ibar, struct MuttWindow *win_pager, struct MuttWindow *win_pbar, struct Mailbox *m, struct Email *e)
Display a message in the pager.
Definition: commands.c:206
Colors
Definition: color.h:129
mutt_collapse_thread
#define mutt_collapse_thread(e)
Definition: mutt_thread.h:89
Email::display_subject
bool display_subject
Used for threading.
Definition: email.h:57
Context::mailbox
struct Mailbox * mailbox
Definition: context.h:50
WT_DLG_INDEX
@ WT_DLG_INDEX
Index Dialog, index_pager_init()
Definition: mutt_window.h:83
ColorLine::pair
int pair
Colour pair index.
Definition: color.h:43
mutt_mem_realloc
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
MUTT_MH
@ MUTT_MH
'MH' Mailbox type
Definition: mailbox.h:50
nm_db_longrun_done
void nm_db_longrun_done(struct Mailbox *m)
Finish a long transaction.
Definition: db.c:306
Email::env
struct Envelope * env
Envelope information.
Definition: email.h:90
mutt_display_address
void mutt_display_address(struct Envelope *env)
Display the address of a message.
Definition: commands.c:927
mutt_debug
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
MuttThread
An Email conversation.
Definition: thread.h:34
update_index
static void update_index(struct Menu *menu, struct Context *ctx, enum MxStatus check, int oldcount, const struct CurrentEmail *cur)
Update the index.
Definition: index.c:608
Mailbox::email_max
int email_max
Number of pointers in emails.
Definition: mailbox.h:100
MUTT_OPEN_NO_FLAGS
#define MUTT_OPEN_NO_FLAGS
No flags are set.
Definition: mx.h:51
SEND_GROUP_REPLY
#define SEND_GROUP_REPLY
Reply to all.
Definition: send.h:42
MUTT_WIN_SIZE_UNLIMITED
#define MUTT_WIN_SIZE_UNLIMITED
Use as much space as possible.
Definition: mutt_window.h:49
ci_previous_undeleted
static int ci_previous_undeleted(struct Mailbox *m, int msgno)
Find the previous undeleted email.
Definition: index.c:311
MessageTransformOpt
MessageTransformOpt
Message transformation option.
Definition: commands.h:49
Menu::tag
int(* tag)(struct Menu *menu, int sel, int act)
Tag some menu items.
Definition: mutt_menu.h:107
mutt_pipe_message
void mutt_pipe_message(struct Mailbox *m, struct EmailList *el)
Pipe a message.
Definition: commands.c:702
Context::collapsed
bool collapsed
Are all threads collapsed?
Definition: context.h:48
notify_observer_add
bool notify_observer_add(struct Notify *notify, enum NotifyType type, observer_t callback, void *global_data)
Add an observer to an object.
Definition: notify.c:169
mutt_set_header_color
void mutt_set_header_color(struct Mailbox *m, struct Email *e)
Select a colour for a message.
Definition: index.c:4091
MUTT_UNTAG
@ MUTT_UNTAG
Messages to be un-tagged.
Definition: mutt.h:104
bool_str_toggle
int bool_str_toggle(struct ConfigSubset *sub, const char *name, struct Buffer *err)
Toggle the value of a bool.
Definition: bool.c:213
SEND_NO_FLAGS
#define SEND_NO_FLAGS
No flags are set.
Definition: send.h:40
Mailbox::realpath
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:84
mutt_thread_collapse
void mutt_thread_collapse(struct ThreadsContext *tctx, bool collapse)
toggle collapse
Definition: mutt_thread.c:1604
REDRAW_INDEX
#define REDRAW_INDEX
Redraw the index.
Definition: mutt_menu.h:40
MX_STATUS_ERROR
@ MX_STATUS_ERROR
An error occurred.
Definition: mx.h:73
Email::flagged
bool flagged
Marked important?
Definition: email.h:43
nntp_select_server
struct NntpAccountData * nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition: newsrc.c:1012
mutt_buffer_string
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
Mailbox::verbose
bool verbose
Display status messages?
Definition: mailbox.h:118
resort_index
static void resort_index(struct Context *ctx, struct Menu *menu)
Resort the index.
Definition: index.c:413
OptRedrawTree
WHERE bool OptRedrawTree
(pseudo) redraw the thread tree
Definition: options.h:50
CurrentNewsSrv
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition: nntp.c:79
WindowState::rows
short rows
Number of rows, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:58
MuttWindow::parent
struct MuttWindow * parent
Parent Window.
Definition: mutt_window.h:128
mutt_mailbox.h
pop_fetch_mail
void pop_fetch_mail(void)
Fetch messages and save them in $spool_file.
Definition: pop.c:509
Email::security
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition: email.h:39
change_folder_string
static void change_folder_string(struct Menu *menu, char *buf, size_t buflen, int *oldcount, const struct CurrentEmail *cur, bool *pager_return, bool read_only)
Change to a different Mailbox by string.
Definition: index.c:824
nm_db_debug_check
void nm_db_debug_check(struct Mailbox *m)
Check if the database is open.
Definition: db.c:324
MUTT_CURSOR_INVISIBLE
@ MUTT_CURSOR_INVISIBLE
Hide the cursor.
Definition: mutt_curses.h:80
Email::collapsed
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:73
SLIST_FIRST
#define SLIST_FIRST(head)
Definition: queue.h:228
index_add_observers
void index_add_observers(struct MuttWindow *dlg)
Add Observers to the Index Dialog.
Definition: observer.c:184
cs_subset_quad
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:154
MuttWindow::help_data
const struct Mapping * help_data
Data for the Help Bar.
Definition: mutt_window.h:135
mutt_enter_command
void mutt_enter_command(void)
enter a neomutt command
Definition: commands.c:878
MUTT_ACL_WRITE
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:74
Menu::top
int top
Entry that is the top of the current page.
Definition: mutt_menu.h:75
mutt_str_replace
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
lib.h
lib.h
mutt_next_subthread
#define mutt_next_subthread(e)
Definition: mutt_thread.h:98
cs_subset_sort
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:219
WithCrypto
#define WithCrypto
Definition: lib.h:123
mutt_uncollapse_thread
#define mutt_uncollapse_thread(e)
Definition: mutt_thread.h:90
mutt_link_threads
bool mutt_link_threads(struct Email *parent, struct EmailList *children, struct Mailbox *m)
Forcibly link threads together.
Definition: mutt_thread.c:1563
mutt_logging.h
mutt_flushinp
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:923
MuttWindow::state
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:120
Context::limit_pattern
struct PatternList * limit_pattern
Compiled limit pattern.
Definition: context.h:42
lib.h
lib.h
mutt_menu_pop_current
void mutt_menu_pop_current(struct Menu *menu)
Remove a Menu from the stack.
Definition: menu.c:1027
WT_INDEX
@ WT_INDEX
An Index Window containing a selection list.
Definition: mutt_window.h:94
menu_status_line
void menu_status_line(char *buf, size_t buflen, struct Menu *menu, struct Mailbox *m, const char *p)
Create the status line.
Definition: status.c:420
menu_redraw_motion
void menu_redraw_motion(struct Menu *menu)
Force the redraw of the list part of the menu.
Definition: menu.c:434
Email::deleted
bool deleted
Email is deleted.
Definition: email.h:45
crypt_forget_passphrase
void crypt_forget_passphrase(void)
Forget a passphrase and display a message.
Definition: crypt.c:93
STAILQ_HEAD_INITIALIZER
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:321
lib.h
cs_subset_string
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:241
Mailbox::readonly
bool readonly
Don't allow changes to the mailbox.
Definition: mailbox.h:119
check_acl
static bool check_acl(struct Mailbox *m, AclFlags acl, const char *msg)
Check the ACLs for a function.
Definition: index.c:206
mutt_mktemp
#define mutt_mktemp(buf, buflen)
Definition: muttlib.h:74
lib.h
mutt_what_key
void mutt_what_key(void)
Ask the user to press a key.
Definition: keymap.c:1676
mutt_str_cat
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:385
mailbox_find_name
struct Mailbox * mailbox_find_name(const char *name)
Find the mailbox with a given name.
Definition: mailbox.c:140
MUTT_SHUTDOWN_HOOK
#define MUTT_SHUTDOWN_HOOK
shutdown-hook: run when leaving NeoMutt
Definition: hook.h:65
Colors::index_list
struct ColorLineList index_list
List of default colours applied to the index.
Definition: color.h:138
Mailbox::compress_info
void * compress_info
Compressed mbox module private data.
Definition: mailbox.h:124
mutt_flush_macro_to_endcond
void mutt_flush_macro_to_endcond(void)
Drop a macro from the input buffer.
Definition: curs_lib.c:894
OpenMailboxFlags
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mx.h:50
WT_CONTAINER
@ WT_CONTAINER
Invisible shaping container Window.
Definition: mutt_window.h:70
mutt_thread.h
MUTT_DELETE
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:98
index_remove_observers
void index_remove_observers(struct MuttWindow *dlg)
Remove Observers from the Index Dialog.
Definition: observer.c:196
sort.h
menu_half_up
void menu_half_up(struct Menu *menu)
Move the focus up half a page in the menu.
Definition: menu.c:754
ci_first_message
static int ci_first_message(struct Mailbox *m)
Get index of first new message.
Definition: index.c:335
adata.h
mutt_make_id_hash
struct HashTable * mutt_make_id_hash(struct Mailbox *m)
Create a Hash Table for message-ids.
Definition: mutt_thread.c:1518
Menu::custom_redraw
void(* custom_redraw)(struct Menu *menu)
Redraw the menu.
Definition: mutt_menu.h:122
WT_INDEX_BAR
@ WT_INDEX_BAR
Index Bar containing status info about the Index.
Definition: mutt_window.h:95
mutt_wstr_trunc
size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
Work out how to truncate a widechar string.
Definition: curs_lib.c:1313
mutt_mem_malloc
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
OptSearchInvalid
WHERE bool OptSearchInvalid
(pseudo) used to invalidate the search pattern
Definition: options.h:52
Menu::max
int max
Number of entries in the menu.
Definition: mutt_menu.h:57
CHECK_ATTACH
#define CHECK_ATTACH
Is the user in message-attach mode?
Definition: index.c:146
Email::tags
struct TagList tags
For drivers that support server tagging.
Definition: email.h:109
MUTT_MAILBOX_CHECK_FORCE
#define MUTT_MAILBOX_CHECK_FORCE
Definition: mutt_mailbox.h:16
change_folder_notmuch
static struct Mailbox * change_folder_notmuch(struct Menu *menu, char *buf, int buflen, int *oldcount, const struct CurrentEmail *cur, bool read_only)
Change to a different Notmuch Mailbox by string.
Definition: index.c:798
menu_current_bottom
void menu_current_bottom(struct Menu *menu)
Move the current selection to the bottom of the window.
Definition: menu.c:879
mutt_select_sort
int mutt_select_sort(bool reverse)
Ask the user for a sort method.
Definition: commands.c:769
TsSupported
bool TsSupported
Terminal Setting is supported.
Definition: terminal.c:43
mx_tags_is_supported
bool mx_tags_is_supported(struct Mailbox *m)
return true if mailbox support tagging
Definition: mx.c:1325
MUTT_TAG
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:103
EVM_EDIT
@ EVM_EDIT
Edit the message.
Definition: protos.h:57
mailbox_index_observer
static int mailbox_index_observer(struct NotifyCallback *nc)
Listen for Mailbox changes - Implements observer_t.
Definition: index.c:666
progress.h
MENU_MAIN
@ MENU_MAIN
Index panel (list of emails)
Definition: keymap.h:80
NeoMutt
Container for Accounts, Notifications.
Definition: neomutt.h:36
mutt.h
Context::menu
struct Menu * menu
Needed for pattern compilation.
Definition: context.h:46
menu_half_down
void menu_half_down(struct Menu *menu)
Move the focus down half a page in the menu.
Definition: menu.c:745
nm_message_is_still_queried
bool nm_message_is_still_queried(struct Mailbox *m, struct Email *e)
Is a message still visible in the query?
Definition: notmuch.c:1766
menu_current_middle
void menu_current_middle(struct Menu *menu)
Move the current selection to the centre of the window.
Definition: menu.c:861
mx_mbox_open
struct Context * mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:309
mutt_curses_set_color
void mutt_curses_set_color(enum ColorId color)
Set the current colour for text.
Definition: mutt_curses.c:57
recvattach.h
Menu::redraw
MuttRedrawFlags redraw
When to redraw the screen.
Definition: mutt_menu.h:58
km_error_key
void km_error_key(enum MenuType menu)
Handle an unbound key sequence.
Definition: keymap.c:1137
Email::vnum
int vnum
Virtual message number.
Definition: email.h:88
REDRAW_CURRENT
#define REDRAW_CURRENT
Redraw the current line of the menu.
Definition: mutt_menu.h:43
create_panel_index
static struct MuttWindow * create_panel_index(struct MuttWindow *parent, bool status_on_top)
Create the Windows for the Index panel.
Definition: index.c:4117
ListNode::data
char * data
String.
Definition: list.h:36
MUTT_CURSOR_VISIBLE
@ MUTT_CURSOR_VISIBLE
Display a normal cursor.
Definition: mutt_curses.h:81
MailboxType
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
MUTT_WIN_SIZE_FIXED
@ MUTT_WIN_SIZE_FIXED
Window has a fixed size.
Definition: mutt_window.h:44
context.h
ColorLine::match
int match
Substring to match, 0 for old behaviour.
Definition: color.h:38
ColorLine
A regular expression and a color to highlight a line.
Definition: color.h:35
nm_read_entire_thread
int nm_read_entire_thread(struct Mailbox *m, struct Email *e)
Get the entire thread of an email.
Definition: notmuch.c:1578
Email::index
int index
The absolute (unsorted) message number.
Definition: email.h:86
mutt_refresh
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:108
mutt_previous_thread
#define mutt_previous_thread(e)
Definition: mutt_thread.h:97
NeoMutt::sub
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
ColorLine::color_pattern
struct PatternList * color_pattern
Compiled pattern to speed up index color calculation.
Definition: color.h:40
browser.h
CurrentFolder
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: mutt_globals.h:54
menu_next_page
void menu_next_page(struct Menu *menu)
Move the focus to the next page in the menu.
Definition: menu.c:727
mutt_progress_update
void mutt_progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:212
lib.h
mutt_window_mvaddstr
int mutt_window_mvaddstr(struct MuttWindow *win, int col, int row, const char *str)
Move the cursor and write a fixed string to a Window.
Definition: mutt_window.c:396
SEND_LIST_REPLY
#define SEND_LIST_REPLY
Reply to mailing list.
Definition: send.h:43
Mailbox::msg_tagged
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:97
Context::pattern
char * pattern
Limit pattern string.
Definition: context.h:41
STAILQ_NEXT
#define STAILQ_NEXT(elm, field)
Definition: queue.h:397
index_custom_redraw
static void index_custom_redraw(struct Menu *menu)
Redraw the index - Implements Menu::custom_redraw()
Definition: index.c:1100
mutt_get_field
int mutt_get_field(const char *field, char *buf, size_t buflen, CompletionFlags complete, bool multiple, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:311
mutt_clear_pager_position
void mutt_clear_pager_position(void)
Reset the pager's viewing position.
Definition: pager.c:2063
MUTT_MAILDIR
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition: mailbox.h:51
crypt_extract_keys_from_messages
void crypt_extract_keys_from_messages(struct Mailbox *m, struct EmailList *el)
Extract keys from a message.
Definition: crypt.c:846
score.h
km_dokey
int km_dokey(enum MenuType menu)
Determine what a keypress should do.
Definition: keymap.c:658
WindowState::cols
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:57
menu_redraw_full
void menu_redraw_full(struct Menu *menu)
Force the redraw of the Menu.
Definition: menu.c:348
MX_STATUS_FLAGS
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition: mx.h:78
Email::attach_del
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:49
mailbox_path
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:206
mutt_buffer_enter_fname
int mutt_buffer_enter_fname(const char *prompt, struct Buffer *fname, bool mailbox, struct Mailbox *m, bool multiple, char ***files, int *numfiles, SelectFileFlags flags)
Ask the user to select a file.
Definition: curs_lib.c:772
NotifyCallback::event_subtype
int event_subtype
Send: Event subtype, e.g. NT_ACCOUNT_ADD.
Definition: observer.h:43
Menu::current
int current
Current entry.
Definition: mutt_menu.h:56
Colors::defs
int * defs
Array of all fixed colours, see enum ColorId.
Definition: color.h:131
mx_fastclose_mailbox
void mx_fastclose_mailbox(struct Mailbox *m)
free up memory associated with the Mailbox
Definition: mx.c:451
mx.h
NotifyCallback::event_type
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition: observer.h:42
commands.h
mutt_window_add_child
void mutt_window_add_child(struct MuttWindow *parent, struct MuttWindow *child)
Add a child to Window.
Definition: mutt_window.c:565
opcodes.h
MUTT_PAGER_LOGS
#define MUTT_PAGER_LOGS
Logview mode.
Definition: lib.h:56
private.h
REDRAW_NO_FLAGS
#define REDRAW_NO_FLAGS
No flags are set.
Definition: mutt_menu.h:39
update_index_unthreaded
static void update_index_unthreaded(struct Context *ctx, enum MxStatus check)
Update the index (if unthreaded)
Definition: index.c:532
CheckFlags
uint8_t CheckFlags
Checks to perform before running a function.
Definition: index.c:140
lib.h
lib.h
mutt_break_thread
void mutt_break_thread(struct Email *e)
Break the email Thread.
Definition: thread.c:225
Buffer::data
char * data
Pointer to data.
Definition: buffer.h:35
Email
The envelope/body of an email.
Definition: email.h:37
MUTT_ACL_SEEN
#define MUTT_ACL_SEEN
Change the 'seen' status of a message.
Definition: mailbox.h:73
lib.h
Colors::status_list
struct ColorLineList status_list
List of colours applied to the status bar.
Definition: color.h:141
mailbox_gc_run
void mailbox_gc_run(void)
Definition: mailbox.c:255
REDRAW_MOTION
#define REDRAW_MOTION
Redraw after moving the menu list.
Definition: mutt_menu.h:41
NT_MAILBOX_CLOSED
@ NT_MAILBOX_CLOSED
Mailbox was closed.
Definition: mailbox.h:172
mutt_message
#define mutt_message(...)
Definition: logging.h:83
mutt_set_flag
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:67
mutt_unget_event
void mutt_unget_event(int ch, int op)
Return a keystroke to the input buffer.
Definition: curs_lib.c:838
mutt_browser_select_dir
void mutt_browser_select_dir(const char *f)
Remember the last directory selected.
Definition: browser.c:1122
emaillist_clear
void emaillist_clear(struct EmailList *el)
Drop a private list of Emails.
Definition: email.c:130
mx_msg_padding_size
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size()
Definition: mx.c:1554
mutt_make_version
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1462
Envelope::references
struct ListHead references
message references (in reverse order)
Definition: envelope.h:81
Menu::win_index
struct MuttWindow * win_index
Definition: mutt_menu.h:63
create_panel_pager
static struct MuttWindow * create_panel_pager(struct MuttWindow *parent, bool status_on_top)
Create the Windows for the Pager panel.
Definition: index.c:4153
mutt_check_stats
void mutt_check_stats(struct Mailbox *m)
Forcibly update mailbox stats.
Definition: commands.c:1483
Menu
GUI selectable list of items.
Definition: mutt_menu.h:52
mutt_newsgroup_catchup
struct NntpMboxData * mutt_newsgroup_catchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Catchup newsgroup.
Definition: newsrc.c:1297
IndexNewsHelp
static const struct Mapping IndexNewsHelp[]
Help Bar for the News Index dialog.
Definition: index.c:121
Email::read
bool read
Email is read.
Definition: email.h:51
hook.h
N_
#define N_(a)
Definition: message.h:32
MUTT_NM_QUERY
#define MUTT_NM_QUERY
Notmuch query mode.
Definition: mutt.h:66
prereq
static bool prereq(struct Context *ctx, struct Menu *menu, CheckFlags checks)
Check the pre-requisites for a function.
Definition: index.c:156
mutt_buffer_strcpy
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
Mailbox::id_hash
struct HashTable * id_hash
Hash Table by msg id.
Definition: mailbox.h:127
index_pager_shutdown
void index_pager_shutdown(struct MuttWindow *dlg)
Clear up any non-Window parts.
Definition: index.c:4205
menu_top_page
void menu_top_page(struct Menu *menu)
Move the focus to the top of the page.
Definition: menu.c:763
EventMailbox::mailbox
struct Mailbox * mailbox
The Mailbox this Event relates to.
Definition: mailbox.h:185
query_index
void query_index(struct ConfigSubset *sub)
Perform an Alias Query and display the results.
Definition: dlgquery.c:646
REDRAW_STATUS
#define REDRAW_STATUS
Redraw the status bar.
Definition: mutt_menu.h:44
status.h
Email::quasi_deleted
bool quasi_deleted
Deleted from neomutt, but not modified on disk.
Definition: email.h:47
hdrline.h
CurrentEmail::sequence
size_t sequence
Sequence of the current email.
Definition: index.c:574
Menu::color
int(* color)(struct Menu *menu, int line)
Calculate the colour for a line of the menu.
Definition: mutt_menu.h:116
menu_prev_page
void menu_prev_page(struct Menu *menu)
Move the focus to the previous page in the menu.
Definition: menu.c:736
mutt_thread_next_unread
#define mutt_thread_next_unread(e)
Definition: mutt_thread.h:93
Mailbox::pathbuf
struct Buffer pathbuf
Definition: mailbox.h:83
NntpMboxData
NNTP-specific Mailbox data -.
Definition: mdata.h:31
ci_next_undeleted
static int ci_next_undeleted(struct Mailbox *m, int msgno)
Find the next undeleted email.
Definition: index.c:288
mx_mbox_find2
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1654
PatternCache
Cache commonly-used patterns.
Definition: lib.h:109
imap_check_mailbox
enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
use the NOOP or IDLE command to poll for new mail
Definition: imap.c:1094
MuttFormatFlags
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
mutt_emails_set_flag
void mutt_emails_set_flag(struct Mailbox *m, struct EmailList *el, enum MessageType flag, bool bf)
Set flag on messages.
Definition: flags.c:354
MX_STATUS_OK
@ MX_STATUS_OK
No changes.
Definition: mx.h:74
driver_tags_get_with_hidden
char * driver_tags_get_with_hidden(struct TagList *list)
Get tags with hiddens.
Definition: tags.c:157
Email::pair
int pair
Color-pair to use when displaying in the index.
Definition: email.h:80
mutt_thread_contains_unread
#define mutt_thread_contains_unread(e)
Definition: mutt_thread.h:91
Email::body
struct Body * body
List of MIME parts.
Definition: email.h:91
menu_first_entry
void menu_first_entry(struct Menu *menu)
Move the focus to the first entry in the menu.
Definition: menu.c:813
mutt_index_menu
int mutt_index_menu(struct MuttWindow *dlg)
Display a list of emails.
Definition: index.c:1160
OptNews
WHERE bool OptNews
(pseudo) used to change reader mode
Definition: options.h:45
mutt_menu_free
void mutt_menu_free(struct Menu **ptr)
Destroy a menu.
Definition: menu.c:972
MIN
#define MIN(a, b)
Definition: memory.h:31
mutt_error
#define mutt_error(...)
Definition: logging.h:84
mutt_window_reflow
void mutt_window_reflow(struct MuttWindow *win)
Resize a Window and its children.
Definition: mutt_window.c:433
mutt_get_address
struct AddressList * mutt_get_address(struct Envelope *env, const char **prefix)
Get an Address from an Envelope.
Definition: alias.c:326
mutt_str_copy
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:716
MUTT_COMP_NO_FLAGS
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:56