NeoMutt  2021-02-05-329-g9e03b7
Teaching an old dog new tricks
DOXYGEN
index.c File Reference

GUI manage the main index (list of emails) More...

#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "private.h"
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "alias/lib.h"
#include "conn/lib.h"
#include "gui/lib.h"
#include "mutt.h"
#include "lib.h"
#include "menu/lib.h"
#include "ncrypt/lib.h"
#include "pager/lib.h"
#include "pattern/lib.h"
#include "send/lib.h"
#include "browser.h"
#include "commands.h"
#include "context.h"
#include "format_flags.h"
#include "hdrline.h"
#include "hook.h"
#include "keymap.h"
#include "mutt_globals.h"
#include "mutt_header.h"
#include "mutt_logging.h"
#include "mutt_mailbox.h"
#include "mutt_thread.h"
#include "muttlib.h"
#include "mx.h"
#include "opcodes.h"
#include "options.h"
#include "private_data.h"
#include "progress.h"
#include "protos.h"
#include "recvattach.h"
#include "score.h"
#include "shared_data.h"
#include "sort.h"
#include "status.h"
#include "sidebar/lib.h"
#include "pop/lib.h"
#include "imap/lib.h"
#include "notmuch/lib.h"
#include "nntp/lib.h"
#include "nntp/adata.h"
#include "nntp/mdata.h"
#include <libintl.h>
#include "monitor.h"
#include "autocrypt/lib.h"

Go to the source code of this file.

Macros

#define CHECK_NO_FLAGS   0
 No flags are set. More...
 
#define CHECK_IN_MAILBOX   (1 << 0)
 Is there a mailbox open? More...
 
#define CHECK_MSGCOUNT   (1 << 1)
 Are there any messages? More...
 
#define CHECK_VISIBLE   (1 << 2)
 Is the selected message visible in the index? More...
 
#define CHECK_READONLY   (1 << 3)
 Is the mailbox readonly? More...
 
#define CHECK_ATTACH   (1 << 4)
 Is the user in message-attach mode? More...
 

Typedefs

typedef uint8_t CheckFlags
 Checks to perform before running a function. More...
 

Functions

static bool prereq (struct Context *ctx, struct Menu *menu, CheckFlags checks)
 Check the pre-requisites for a function. More...
 
static bool check_acl (struct Mailbox *m, AclFlags acl, const char *msg)
 Check the ACLs for a function. More...
 
static void collapse_all (struct Context *ctx, struct Menu *menu, int toggle)
 Collapse/uncollapse all threads. More...
 
static int ci_next_undeleted (struct Mailbox *m, int msgno)
 Find the next undeleted email. More...
 
static int ci_previous_undeleted (struct Mailbox *m, int msgno)
 Find the previous undeleted email. More...
 
static int ci_first_message (struct Mailbox *m)
 Get index of first new message. More...
 
static int mx_toggle_write (struct Mailbox *m)
 Toggle the mailbox's readonly flag. More...
 
static void resort_index (struct Context *ctx, struct Menu *menu)
 Resort the index. More...
 
static void update_index_threaded (struct Context *ctx, enum MxStatus check, int oldcount)
 Update the index (if threaded) More...
 
static void update_index_unthreaded (struct Context *ctx, enum MxStatus check)
 Update the index (if unthreaded) More...
 
static void update_index (struct Menu *menu, struct Context *ctx, enum MxStatus check, int oldcount, const struct IndexSharedData *shared)
 Update the index. More...
 
void mutt_update_index (struct Menu *menu, struct Context *ctx, enum MxStatus check, int oldcount, struct IndexSharedData *shared)
 Update the index. More...
 
static int mailbox_index_observer (struct NotifyCallback *nc)
 Listen for Mailbox changes - Implements observer_t. More...
 
static void change_folder_mailbox (struct Menu *menu, struct Mailbox *m, int *oldcount, struct IndexSharedData *shared, bool read_only)
 Change to a different Mailbox by pointer. More...
 
static struct Mailboxchange_folder_notmuch (struct Menu *menu, char *buf, int buflen, int *oldcount, struct IndexSharedData *shared, bool read_only)
 Change to a different Notmuch Mailbox by string. More...
 
static void change_folder_string (struct Menu *menu, char *buf, size_t buflen, int *oldcount, struct IndexSharedData *shared, bool *pager_return, bool read_only)
 Change to a different Mailbox by string. More...
 
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() More...
 
int index_color (struct Menu *menu, int line)
 Calculate the colour for a line of the index - Implements Menu::color() More...
 
void mutt_draw_statusline (int cols, const char *buf, size_t buflen)
 Draw a highlighted status bar. More...
 
static void index_custom_redraw (struct Menu *menu)
 Redraw the index - Implements Menu::custom_redraw() More...
 
struct Mailboxmutt_index_menu (struct MuttWindow *dlg, struct Mailbox *m_init)
 Display a list of emails. More...
 
void mutt_set_header_color (struct Mailbox *m, struct Email *e)
 Select a colour for a message. More...
 
static struct MuttWindowcreate_panel_index (struct MuttWindow *parent, bool status_on_top)
 Create the Windows for the Index panel. More...
 
struct MuttWindowindex_pager_init (void)
 Allocate the Windows for the Index/Pager. More...
 
void index_pager_shutdown (struct MuttWindow *dlg)
 Clear up any non-Window parts. More...
 

Variables

static const struct Mapping IndexHelp []
 Help Bar for the Index dialog. More...
 
static const struct Mapping IndexNewsHelp []
 Help Bar for the News Index dialog. More...
 

Detailed Description

GUI manage the main index (list of emails)

Authors
  • Michael R. Elkins
  • R Primus

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file index.c.

Macro Definition Documentation

◆ CHECK_NO_FLAGS

#define CHECK_NO_FLAGS   0

No flags are set.

Definition at line 144 of file index.c.

◆ CHECK_IN_MAILBOX

#define CHECK_IN_MAILBOX   (1 << 0)

Is there a mailbox open?

Definition at line 145 of file index.c.

◆ CHECK_MSGCOUNT

#define CHECK_MSGCOUNT   (1 << 1)

Are there any messages?

Definition at line 146 of file index.c.

◆ CHECK_VISIBLE

#define CHECK_VISIBLE   (1 << 2)

Is the selected message visible in the index?

Definition at line 147 of file index.c.

◆ CHECK_READONLY

#define CHECK_READONLY   (1 << 3)

Is the mailbox readonly?

Definition at line 148 of file index.c.

◆ CHECK_ATTACH

#define CHECK_ATTACH   (1 << 4)

Is the user in message-attach mode?

Definition at line 149 of file index.c.

Typedef Documentation

◆ CheckFlags

typedef uint8_t CheckFlags

Checks to perform before running a function.

Flags, e.g. CHECK_IN_MAILBOX

Definition at line 143 of file index.c.

Function Documentation

◆ prereq()

static bool prereq ( struct Context ctx,
struct Menu menu,
CheckFlags  checks 
)
static

Check the pre-requisites for a function.

Parameters
ctxMailbox
menuCurrent Menu
checksChecks to perform, see CheckFlags
Return values
trueThe checks pass successfully

Definition at line 159 of file index.c.

160 {
161  bool result = true;
162 
163  if (checks & (CHECK_MSGCOUNT | CHECK_VISIBLE | CHECK_READONLY))
164  checks |= CHECK_IN_MAILBOX;
165 
166  if ((checks & CHECK_IN_MAILBOX) && (!ctx || !ctx->mailbox))
167  {
168  mutt_error(_("No mailbox is open"));
169  result = false;
170  }
171 
172  if (result && (checks & CHECK_MSGCOUNT) && (ctx->mailbox->msg_count == 0))
173  {
174  mutt_error(_("There are no messages"));
175  result = false;
176  }
177 
178  int index = menu_get_index(menu);
179  if (result && (checks & CHECK_VISIBLE) && (index >= ctx->mailbox->vcount))
180  {
181  mutt_error(_("No visible messages"));
182  result = false;
183  }
184 
185  if (result && (checks & CHECK_READONLY) && ctx->mailbox->readonly)
186  {
187  mutt_error(_("Mailbox is read-only"));
188  result = false;
189  }
190 
191  if (result && (checks & CHECK_ATTACH) && OptAttachMsg)
192  {
193  mutt_error(_("Function not permitted in attach-message mode"));
194  result = false;
195  }
196 
197  if (!result)
198  mutt_flushinp();
199 
200  return result;
201 }
int msg_count
Total number of messages.
Definition: mailbox.h:91
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:810
#define CHECK_ATTACH
Is the user in message-attach mode?
Definition: index.c:149
#define _(a)
Definition: message.h:28
int vcount
The number of virtual messages.
Definition: mailbox.h:102
#define CHECK_VISIBLE
Is the selected message visible in the index?
Definition: index.c:147
struct Mailbox * mailbox
Definition: context.h:49
#define CHECK_IN_MAILBOX
Is there a mailbox open?
Definition: index.c:145
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:119
WHERE bool OptAttachMsg
(pseudo) used by attach-message
Definition: options.h:31
#define CHECK_READONLY
Is the mailbox readonly?
Definition: index.c:148
#define mutt_error(...)
Definition: logging.h:84
#define CHECK_MSGCOUNT
Are there any messages?
Definition: index.c:146
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ check_acl()

static bool check_acl ( struct Mailbox m,
AclFlags  acl,
const char *  msg 
)
static

Check the ACLs for a function.

Parameters
mMailbox
aclACL, see AclFlags
msgError message for failure
Return values
trueThe function is permitted

Definition at line 210 of file index.c.

211 {
212  if (!m)
213  return false;
214 
215  if (!(m->rights & acl))
216  {
217  /* L10N: %s is one of the CHECK_ACL entries below. */
218  mutt_error(_("%s: Operation not permitted by ACL"), msg);
219  return false;
220  }
221 
222  return true;
223 }
#define _(a)
Definition: message.h:28
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:121
#define mutt_error(...)
Definition: logging.h:84
+ Here is the caller graph for this function:

◆ collapse_all()

static void collapse_all ( struct Context ctx,
struct Menu menu,
int  toggle 
)
static

Collapse/uncollapse all threads.

Parameters
ctxContext
menucurrent menu
toggletoggle collapsed state

This function is called by the OP_MAIN_COLLAPSE_ALL command and on folder enter if the $collapse_all option is set. In the first case, the toggle parameter is 1 to actually toggle collapsed/uncollapsed state on all threads. In the second case, the toggle parameter is 0, actually turning this function into a one-way collapse.

Definition at line 237 of file index.c.

238 {
239  if (!ctx || !ctx->mailbox || (ctx->mailbox->msg_count == 0) || !menu)
240  return;
241 
242  struct Email *e_cur = mutt_get_virt_email(ctx->mailbox, menu_get_index(menu));
243  if (!e_cur)
244  return;
245 
246  int final;
247 
248  /* Figure out what the current message would be after folding / unfolding,
249  * so that we can restore the cursor in a sane way afterwards. */
250  if (e_cur->collapsed && toggle)
251  final = mutt_uncollapse_thread(e_cur);
252  else if (mutt_thread_can_collapse(e_cur))
253  final = mutt_collapse_thread(e_cur);
254  else
255  final = e_cur->vnum;
256 
257  if (final == -1)
258  return;
259 
260  struct Email *base = mutt_get_virt_email(ctx->mailbox, final);
261  if (!base)
262  return;
263 
264  /* Iterate all threads, perform collapse/uncollapse as needed */
265  ctx->collapsed = toggle ? !ctx->collapsed : true;
267 
268  /* Restore the cursor */
269  mutt_set_vnum(ctx->mailbox);
270  for (int i = 0; i < ctx->mailbox->vcount; i++)
271  {
272  struct Email *e = mutt_get_virt_email(ctx->mailbox, i);
273  if (!e)
274  break;
275  if (e->index == base->index)
276  {
277  menu_set_index(menu, i);
278  break;
279  }
280  }
281 
283 }
int msg_count
Total number of messages.
Definition: mailbox.h:91
void mutt_thread_collapse(struct ThreadsContext *tctx, bool collapse)
toggle collapse
Definition: mutt_thread.c:1624
The envelope/body of an email.
Definition: email.h:37
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:408
#define mutt_uncollapse_thread(e)
Definition: mutt_thread.h:76
#define mutt_collapse_thread(e)
Definition: mutt_thread.h:75
struct ThreadsContext * threads
Threads context.
Definition: context.h:42
int vcount
The number of virtual messages.
Definition: mailbox.h:102
struct Mailbox * mailbox
Definition: context.h:49
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:73
int vnum
Virtual message number.
Definition: email.h:88
bool collapsed
Are all threads collapsed?
Definition: context.h:47
int index
The absolute (unsorted) message number.
Definition: email.h:86
off_t mutt_set_vnum(struct Mailbox *m)
Set the virtual index number of all the messages in a mailbox.
Definition: mutt_thread.c:1250
bool mutt_thread_can_collapse(struct Email *e)
Check whether a thread can be collapsed.
Definition: mutt_thread.c:1652
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ci_next_undeleted()

static int ci_next_undeleted ( struct Mailbox m,
int  msgno 
)
static

Find the next undeleted email.

Parameters
mMailbox
msgnoMessage number to start at
Return values
>=0Message number of next undeleted email
-1No more undeleted messages

Definition at line 292 of file index.c.

293 {
294  if (!m)
295  return -1;
296 
297  for (int i = msgno + 1; i < m->vcount; i++)
298  {
299  struct Email *e = mutt_get_virt_email(m, i);
300  if (!e)
301  continue;
302  if (!e->deleted)
303  return i;
304  }
305  return -1;
306 }
The envelope/body of an email.
Definition: email.h:37
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:408
int vcount
The number of virtual messages.
Definition: mailbox.h:102
bool deleted
Email is deleted.
Definition: email.h:45
int msgno
Number displayed to the user.
Definition: email.h:87
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ci_previous_undeleted()

static int ci_previous_undeleted ( struct Mailbox m,
int  msgno 
)
static

Find the previous undeleted email.

Parameters
mMailbox
msgnoMessage number to start at
Return values
>=0Message number of next undeleted email
-1No more undeleted messages

Definition at line 315 of file index.c.

316 {
317  if (!m)
318  return -1;
319 
320  for (int i = msgno - 1; i >= 0; i--)
321  {
322  struct Email *e = mutt_get_virt_email(m, i);
323  if (!e)
324  continue;
325  if (!e->deleted)
326  return i;
327  }
328  return -1;
329 }
The envelope/body of an email.
Definition: email.h:37
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:408
bool deleted
Email is deleted.
Definition: email.h:45
int msgno
Number displayed to the user.
Definition: email.h:87
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ci_first_message()

static int ci_first_message ( struct Mailbox m)
static

Get index of first new message.

Parameters
mMailbox
Return values
numIndex of first new message

Return the index of the first new message, or failing that, the first unread message.

Definition at line 339 of file index.c.

340 {
341  if (!m || (m->msg_count == 0))
342  return 0;
343 
344  int old = -1;
345  for (int i = 0; i < m->vcount; i++)
346  {
347  struct Email *e = mutt_get_virt_email(m, i);
348  if (!e)
349  continue;
350  if (!e->read && !e->deleted)
351  {
352  if (!e->old)
353  return i;
354  if (old == -1)
355  old = i;
356  }
357  }
358  if (old != -1)
359  return old;
360 
361  /* If `$sort` is reverse and not threaded, the latest message is first.
362  * If `$sort` is threaded, the latest message is first if exactly one
363  * of `$sort` and `$sort_aux` are reverse. */
364  const short c_sort = cs_subset_sort(m->sub, "sort");
365  const short c_sort_aux = cs_subset_sort(m->sub, "sort_aux");
366  if (((c_sort & SORT_REVERSE) && ((c_sort & SORT_MASK) != SORT_THREADS)) ||
367  (((c_sort & SORT_MASK) == SORT_THREADS) && ((c_sort ^ c_sort_aux) & SORT_REVERSE)))
368  {
369  return 0;
370  }
371  else
372  {
373  return m->vcount ? m->vcount - 1 : 0;
374  }
375 
376  return 0;
377 }
int msg_count
Total number of messages.
Definition: mailbox.h:91
The envelope/body of an email.
Definition: email.h:37
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:408
int vcount
The number of virtual messages.
Definition: mailbox.h:102
bool read
Email is read.
Definition: email.h:51
bool old
Email is seen, but unread.
Definition: email.h:50
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:272
Sort by email threads.
Definition: sort2.h:49
struct ConfigSubset * sub
Inherited config items.
Definition: mailbox.h:86
bool deleted
Email is deleted.
Definition: email.h:45
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:79
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:78
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mx_toggle_write()

static int mx_toggle_write ( struct Mailbox m)
static

Toggle the mailbox's readonly flag.

Parameters
mMailbox
Return values
0Success
-1Error

This should be in mx.c, but it only gets used here.

Definition at line 387 of file index.c.

388 {
389  if (!m)
390  return -1;
391 
392  if (m->readonly)
393  {
394  mutt_error(_("Can't toggle write on a readonly mailbox"));
395  return -1;
396  }
397 
398  if (m->dontwrite)
399  {
400  m->dontwrite = false;
401  mutt_message(_("Changes to folder will be written on folder exit"));
402  }
403  else
404  {
405  m->dontwrite = true;
406  mutt_message(_("Changes to folder will not be written"));
407  }
408 
409  return 0;
410 }
#define mutt_message(...)
Definition: logging.h:83
#define _(a)
Definition: message.h:28
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:119
bool dontwrite
Don&#39;t write the mailbox on close.
Definition: mailbox.h:115
#define mutt_error(...)
Definition: logging.h:84
+ Here is the caller graph for this function:

◆ resort_index()

static void resort_index ( struct Context ctx,
struct Menu menu 
)
static

Resort the index.

Parameters
ctxContext
menuCurrent Menu

Definition at line 417 of file index.c.

418 {
419  if (!ctx || !ctx->mailbox || !menu)
420  return;
421 
422  struct Mailbox *m = ctx->mailbox;
423  const int old_index = menu_get_index(menu);
424  struct Email *e_cur = mutt_get_virt_email(m, old_index);
425 
426  int new_index = -1;
427  mutt_sort_headers(m, ctx->threads, false, &ctx->vsize);
428  /* Restore the current message */
429 
430  for (int i = 0; i < m->vcount; i++)
431  {
432  struct Email *e = mutt_get_virt_email(m, i);
433  if (!e)
434  continue;
435  if (e == e_cur)
436  {
437  new_index = i;
438  break;
439  }
440  }
441 
442  const short c_sort = cs_subset_sort(m->sub, "sort");
443  if (((c_sort & SORT_MASK) == SORT_THREADS) && (old_index < 0))
444  new_index = mutt_parent_message(e_cur, false);
445 
446  if (old_index < 0)
447  new_index = ci_first_message(m);
448 
449  menu_set_index(menu, new_index);
451 }
static int ci_first_message(struct Mailbox *m)
Get index of first new message.
Definition: index.c:339
The envelope/body of an email.
Definition: email.h:37
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:408
void mutt_sort_headers(struct Mailbox *m, struct ThreadsContext *threads, bool init, off_t *vsize)
Sort emails by their headers.
Definition: sort.c:378
struct ThreadsContext * threads
Threads context.
Definition: context.h:42
int vcount
The number of virtual messages.
Definition: mailbox.h:102
struct Mailbox * mailbox
Definition: context.h:49
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:272
off_t vsize
Size (in bytes) of the messages shown.
Definition: context.h:39
Sort by email threads.
Definition: sort2.h:49
A mailbox.
Definition: mailbox.h:81
struct ConfigSubset * sub
Inherited config items.
Definition: mailbox.h:86
int mutt_parent_message(struct Email *e, bool find_root)
Find the parent of a message.
Definition: mutt_thread.c:1199
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:78
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_index_threaded()

static void update_index_threaded ( struct Context ctx,
enum MxStatus  check,
int  oldcount 
)
static

Update the index (if threaded)

Parameters
ctxMailbox
checkFlags, e.g. MX_STATUS_REOPENED
oldcountHow many items are currently in the index

Definition at line 459 of file index.c.

460 {
461  struct Email **save_new = NULL;
462  const bool lmt = ctx_has_limit(ctx);
463 
464  struct Mailbox *m = ctx->mailbox;
465  int num_new = MAX(0, m->msg_count - oldcount);
466 
467  const bool c_uncollapse_new = cs_subset_bool(m->sub, "uncollapse_new");
468  /* save the list of new messages */
469  if ((check != MX_STATUS_REOPENED) && (oldcount > 0) &&
470  (lmt || c_uncollapse_new) && (num_new > 0))
471  {
472  save_new = mutt_mem_malloc(num_new * sizeof(struct Email *));
473  for (int i = oldcount; i < m->msg_count; i++)
474  save_new[i - oldcount] = m->emails[i];
475  }
476 
477  /* Sort first to thread the new messages, because some patterns
478  * require the threading information.
479  *
480  * If the mailbox was reopened, need to rethread from scratch. */
481  mutt_sort_headers(m, ctx->threads, (check == MX_STATUS_REOPENED), &ctx->vsize);
482 
483  if (lmt)
484  {
485  /* Because threading changes the order in m->emails, we don't
486  * know which emails are new. Hence, we need to re-apply the limit to the
487  * whole set.
488  */
489  for (int i = 0; i < m->msg_count; i++)
490  {
491  struct Email *e = m->emails[i];
492  if ((e->vnum != -1) || mutt_pattern_exec(SLIST_FIRST(ctx->limit_pattern),
493  MUTT_MATCH_FULL_ADDRESS, m, e, NULL))
494  {
495  /* vnum will get properly set by mutt_set_vnum(), which
496  * is called by mutt_sort_headers() just below. */
497  e->vnum = 1;
498  e->visible = true;
499  }
500  else
501  {
502  e->vnum = -1;
503  e->visible = false;
504  }
505  }
506  /* Need a second sort to set virtual numbers and redraw the tree */
507  mutt_sort_headers(m, ctx->threads, false, &ctx->vsize);
508  }
509 
510  /* uncollapse threads with new mail */
511  if (c_uncollapse_new)
512  {
513  if (check == MX_STATUS_REOPENED)
514  {
515  ctx->collapsed = false;
517  mutt_set_vnum(m);
518  }
519  else if (oldcount > 0)
520  {
521  for (int j = 0; j < num_new; j++)
522  {
523  if (save_new[j]->visible)
524  {
525  mutt_uncollapse_thread(save_new[j]);
526  }
527  }
528  mutt_set_vnum(m);
529  }
530  }
531 
532  FREE(&save_new);
533 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
int msg_count
Total number of messages.
Definition: mailbox.h:91
void mutt_thread_collapse(struct ThreadsContext *tctx, bool collapse)
toggle collapse
Definition: mutt_thread.c:1624
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:71
The envelope/body of an email.
Definition: email.h:37
#define mutt_uncollapse_thread(e)
Definition: mutt_thread.h:76
void mutt_sort_headers(struct Mailbox *m, struct ThreadsContext *threads, bool init, off_t *vsize)
Sort emails by their headers.
Definition: sort.c:378
struct ThreadsContext * threads
Threads context.
Definition: context.h:42
bool ctx_has_limit(const struct Context *ctx)
Is a limit active?
Definition: context.c:429
Mailbox was reopened.
Definition: mxapi.h:81
#define MAX(a, b)
Definition: memory.h:30
struct Mailbox * mailbox
Definition: context.h:49
off_t vsize
Size (in bytes) of the messages shown.
Definition: context.h:39
bool visible
Is this message part of the view?
Definition: email.h:74
#define SLIST_FIRST(head)
Definition: queue.h:228
A mailbox.
Definition: mailbox.h:81
struct ConfigSubset * sub
Inherited config items.
Definition: mailbox.h:86
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
struct PatternList * limit_pattern
Compiled limit pattern.
Definition: context.h:41
int vnum
Virtual message number.
Definition: email.h:88
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:1089
bool collapsed
Are all threads collapsed?
Definition: context.h:47
#define FREE(x)
Definition: memory.h:40
off_t mutt_set_vnum(struct Mailbox *m)
Set the virtual index number of all the messages in a mailbox.
Definition: mutt_thread.c:1250
#define MUTT_MATCH_FULL_ADDRESS
Match the full address.
Definition: lib.h:95
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_index_unthreaded()

static void update_index_unthreaded ( struct Context ctx,
enum MxStatus  check 
)
static

Update the index (if unthreaded)

Parameters
ctxMailbox
checkFlags, e.g. MX_STATUS_REOPENED

Definition at line 540 of file index.c.

541 {
542  /* We are in a limited view. Check if the new message(s) satisfy
543  * the limit criteria. If they do, set their virtual msgno so that
544  * they will be visible in the limited view */
545  if (ctx_has_limit(ctx))
546  {
547  int padding = mx_msg_padding_size(ctx->mailbox);
548  ctx->mailbox->vcount = ctx->vsize = 0;
549  for (int i = 0; i < ctx->mailbox->msg_count; i++)
550  {
551  struct Email *e = ctx->mailbox->emails[i];
552  if (!e)
553  break;
555  MUTT_MATCH_FULL_ADDRESS, ctx->mailbox, e, NULL))
556  {
557  assert(ctx->mailbox->vcount < ctx->mailbox->msg_count);
558  e->vnum = ctx->mailbox->vcount;
559  ctx->mailbox->v2r[ctx->mailbox->vcount] = i;
560  e->visible = true;
561  ctx->mailbox->vcount++;
562  struct Body *b = e->body;
563  ctx->vsize += b->length + b->offset - b->hdr_offset + padding;
564  }
565  else
566  {
567  e->visible = false;
568  }
569  }
570  }
571 
572  /* if the mailbox was reopened, need to rethread from scratch */
573  mutt_sort_headers(ctx->mailbox, ctx->threads, (check == MX_STATUS_REOPENED), &ctx->vsize);
574 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
int msg_count
Total number of messages.
Definition: mailbox.h:91
The envelope/body of an email.
Definition: email.h:37
struct Body * body
List of MIME parts.
Definition: email.h:91
void mutt_sort_headers(struct Mailbox *m, struct ThreadsContext *threads, bool init, off_t *vsize)
Sort emails by their headers.
Definition: sort.c:378
struct ThreadsContext * threads
Threads context.
Definition: context.h:42
bool ctx_has_limit(const struct Context *ctx)
Is a limit active?
Definition: context.c:429
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
int vcount
The number of virtual messages.
Definition: mailbox.h:102
The body of an email.
Definition: body.h:34
Mailbox was reopened.
Definition: mxapi.h:81
struct Mailbox * mailbox
Definition: context.h:49
off_t vsize
Size (in bytes) of the messages shown.
Definition: context.h:39
bool visible
Is this message part of the view?
Definition: email.h:74
#define SLIST_FIRST(head)
Definition: queue.h:228
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
struct PatternList * limit_pattern
Compiled limit pattern.
Definition: context.h:41
int vnum
Virtual message number.
Definition: email.h:88
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:1089
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:101
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size()
Definition: mx.c:1539
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:42
#define MUTT_MATCH_FULL_ADDRESS
Match the full address.
Definition: lib.h:95
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_index()

static void update_index ( struct Menu menu,
struct Context ctx,
enum MxStatus  check,
int  oldcount,
const struct IndexSharedData shared 
)
static

Update the index.

Parameters
menuCurrent Menu
ctxMailbox
checkFlags, e.g. MX_STATUS_REOPENED
oldcountHow many items are currently in the index
sharedShared Index data

Definition at line 584 of file index.c.

586 {
587  if (!menu || !ctx)
588  return;
589 
590  struct Mailbox *m = ctx->mailbox;
591  const short c_sort = cs_subset_sort(m->sub, "sort");
592  if ((c_sort & SORT_MASK) == SORT_THREADS)
593  update_index_threaded(ctx, check, oldcount);
594  else
595  update_index_unthreaded(ctx, check);
596 
597  const int old_index = menu_get_index(menu);
598  int index = -1;
599  if (oldcount)
600  {
601  /* restore the current message to the message it was pointing to */
602  for (int i = 0; i < m->vcount; i++)
603  {
604  struct Email *e = mutt_get_virt_email(m, i);
605  if (!e)
606  continue;
607  if (index_shared_data_is_cur_email(shared, e))
608  {
609  index = i;
610  break;
611  }
612  }
613  }
614 
615  if (index < 0)
616  {
617  index = (old_index < m->vcount) ? old_index : ci_first_message(m);
618  }
619  menu_set_index(menu, index);
620 }
static int ci_first_message(struct Mailbox *m)
Get index of first new message.
Definition: index.c:339
bool index_shared_data_is_cur_email(const struct IndexSharedData *shared, const struct Email *e)
Check whether an email is the currently selected Email.
Definition: shared_data.c:220
The envelope/body of an email.
Definition: email.h:37
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:408
int vcount
The number of virtual messages.
Definition: mailbox.h:102
struct Mailbox * mailbox
Definition: context.h:49
static void update_index_threaded(struct Context *ctx, enum MxStatus check, int oldcount)
Update the index (if threaded)
Definition: index.c:459
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:272
Sort by email threads.
Definition: sort2.h:49
A mailbox.
Definition: mailbox.h:81
struct ConfigSubset * sub
Inherited config items.
Definition: mailbox.h:86
static void update_index_unthreaded(struct Context *ctx, enum MxStatus check)
Update the index (if unthreaded)
Definition: index.c:540
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:78
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_update_index()

void mutt_update_index ( struct Menu menu,
struct Context ctx,
enum MxStatus  check,
int  oldcount,
struct IndexSharedData shared 
)

Update the index.

Parameters
menuCurrent Menu
ctxMailbox
checkFlags, e.g. MX_STATUS_REOPENED
oldcountHow many items are currently in the index
sharedShared Index data

Definition at line 630 of file index.c.

632 {
633  update_index(menu, ctx, check, oldcount, shared);
634 }
static void update_index(struct Menu *menu, struct Context *ctx, enum MxStatus check, int oldcount, const struct IndexSharedData *shared)
Update the index.
Definition: index.c:584
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mailbox_index_observer()

static int mailbox_index_observer ( struct NotifyCallback nc)
static

Listen for Mailbox changes - Implements observer_t.

If a Mailbox is closed, then set a pointer to NULL.

Definition at line 641 of file index.c.

642 {
643  if (!nc->global_data)
644  return -1;
645  if ((nc->event_type != NT_MAILBOX) || (nc->event_subtype != NT_MAILBOX_CLOSED))
646  return 0;
647 
648  struct Mailbox **ptr = nc->global_data;
649  if (!ptr || !*ptr)
650  return 0;
651 
652  *ptr = NULL;
653  return 0;
654 }
Mailbox was closed.
Definition: mailbox.h:172
int event_subtype
Send: Event subtype, e.g. NT_ACCOUNT_ADD.
Definition: observer.h:43
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition: observer.h:42
void * global_data
Data from notify_observer_add()
Definition: observer.h:45
A mailbox.
Definition: mailbox.h:81
Mailbox has changed, NotifyMailbox, EventMailbox.
Definition: notify_type.h:47
+ Here is the caller graph for this function:

◆ change_folder_mailbox()

static void change_folder_mailbox ( struct Menu menu,
struct Mailbox m,
int *  oldcount,
struct IndexSharedData shared,
bool  read_only 
)
static

Change to a different Mailbox by pointer.

Parameters
menuCurrent Menu
mMailbox
oldcountHow many items are currently in the index
sharedShared Index data
read_onlyOpen Mailbox in read-only mode

Definition at line 664 of file index.c.

666 {
667  if (!m)
668  return;
669 
670  /* keepalive failure in mutt_enter_fname may kill connection. */
671  if (shared->mailbox && (mutt_buffer_is_empty(&shared->mailbox->pathbuf)))
672  {
673  struct Context *ctx = shared->ctx;
674  index_shared_data_set_context(shared, NULL);
675  ctx_free(&ctx);
676  }
677 
678  if (shared->mailbox)
679  {
680  char *new_last_folder = NULL;
681 #ifdef USE_INOTIFY
682  int monitor_remove_rc = mutt_monitor_remove(NULL);
683 #endif
684 #ifdef USE_COMP_MBOX
685  if (shared->mailbox->compress_info && (shared->mailbox->realpath[0] != '\0'))
686  new_last_folder = mutt_str_dup(shared->mailbox->realpath);
687  else
688 #endif
689  new_last_folder = mutt_str_dup(mailbox_path(shared->mailbox));
690  *oldcount = shared->mailbox->msg_count;
691 
692  const enum MxStatus check = mx_mbox_close(shared->mailbox);
693  if (check == MX_STATUS_OK)
694  {
695  struct Context *ctx = shared->ctx;
696  index_shared_data_set_context(shared, NULL);
697  ctx_free(&ctx);
698  }
699  else
700  {
701 #ifdef USE_INOTIFY
702  if (monitor_remove_rc == 0)
703  mutt_monitor_add(NULL);
704 #endif
705  if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
706  update_index(menu, shared->ctx, check, *oldcount, shared);
707 
708  FREE(&new_last_folder);
709  OptSearchInvalid = true;
711  return;
712  }
713  FREE(&LastFolder);
714  LastFolder = new_last_folder;
715  }
717 
718  /* If the `folder-hook` were to call `unmailboxes`, then the Mailbox (`m`)
719  * could be deleted, leaving `m` dangling. */
720  // TODO: Refactor this function to avoid the need for an observer
722  char *dup_path = mutt_str_dup(mailbox_path(m));
723  char *dup_name = mutt_str_dup(m->name);
724 
725  mutt_folder_hook(dup_path, dup_name);
726  if (m)
727  {
728  /* `m` is still valid, but we won't need the observer again before the end
729  * of the function. */
731  }
732 
733  // Recreate the Mailbox as the folder-hook might have invoked `mailboxes`
734  // and/or `unmailboxes`.
735  m = mx_path_resolve(dup_path);
736  FREE(&dup_path);
737  FREE(&dup_name);
738 
739  if (!m)
740  return;
741 
742  const OpenMailboxFlags flags = read_only ? MUTT_READONLY : MUTT_OPEN_NO_FLAGS;
743  if (mx_mbox_open(m, flags))
744  {
745  struct Context *ctx = ctx_new(m);
746  index_shared_data_set_context(shared, ctx);
747 
748  menu_set_index(menu, ci_first_message(shared->mailbox));
749 #ifdef USE_INOTIFY
750  mutt_monitor_add(NULL);
751 #endif
752  }
753  else
754  {
755  index_shared_data_set_context(shared, NULL);
756  menu_set_index(menu, 0);
757  }
758 
759  const short c_sort = cs_subset_sort(shared->sub, "sort");
760  const bool c_collapse_all = cs_subset_bool(shared->sub, "collapse_all");
761  if (((c_sort & SORT_MASK) == SORT_THREADS) && c_collapse_all)
762  collapse_all(shared->ctx, menu, 0);
763 
764  struct MuttWindow *dlg = dialog_find(menu->win_index);
765  struct EventMailbox em = { shared->mailbox };
767 
769  /* force the mailbox check after we have changed the folder */
772  OptSearchInvalid = true;
773 }
The "current" mailbox.
Definition: context.h:37
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:206
static int ci_first_message(struct Mailbox *m)
Get index of first new message.
Definition: index.c:339
int msg_count
Total number of messages.
Definition: mailbox.h:91
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:71
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:303
struct Mailbox * mailbox
The Mailbox this Event relates to.
Definition: mailbox.h:185
struct ConfigSubset * sub
Config set to use.
Definition: shared_data.h:38
int mutt_monitor_remove(struct Mailbox *m)
Remove a watch for a mailbox.
Definition: monitor.c:526
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:84
New mail received in Mailbox.
Definition: mxapi.h:79
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
A division of the screen.
Definition: mutt_window.h:117
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:609
An Event that happened to a Mailbox.
Definition: mailbox.h:183
int mutt_monitor_add(struct Mailbox *m)
Add a watch for a mailbox.
Definition: monitor.c:481
Mailbox was reopened.
Definition: mxapi.h:81
void mutt_folder_hook(const char *path, const char *desc)
Perform a folder hook.
Definition: hook.c:529
char * name
A short name for the Mailbox.
Definition: mailbox.h:85
struct Notify * notify
Notifications: NotifyWindow, EventWindow.
Definition: mutt_window.h:133
static void update_index(struct Menu *menu, struct Context *ctx, enum MxStatus check, int oldcount, const struct IndexSharedData *shared)
Update the index.
Definition: index.c:584
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:112
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:173
struct Context * ctx
Current Mailbox view.
Definition: shared_data.h:39
static int mailbox_index_observer(struct NotifyCallback *nc)
Listen for Mailbox changes - Implements observer_t.
Definition: index.c:641
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:272
Sort by email threads.
Definition: sort2.h:49
struct Context * ctx_new(struct Mailbox *m)
Create a new Context.
Definition: context.c:75
int mutt_mailbox_check(struct Mailbox *m_cur, int force)
Check all all Mailboxes for new mail.
Definition: mutt_mailbox.c:137
No changes.
Definition: mxapi.h:78
#define MUTT_READONLY
Open in read-only mode.
Definition: mxapi.h:63
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mxapi.h:59
void index_shared_data_set_context(struct IndexSharedData *shared, struct Context *ctx)
Set the Context for the Index and friends.
Definition: shared_data.c:149
WHERE bool OptSearchInvalid
(pseudo) used to invalidate the search pattern
Definition: options.h:52
WHERE char * LastFolder
Previously selected mailbox.
Definition: mutt_globals.h:51
#define MUTT_MAILBOX_CHECK_FORCE
Definition: mutt_mailbox.h:32
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
Current Mailbox has changed.
Definition: mailbox.h:175
void * compress_info
Compressed mbox module private data.
Definition: mailbox.h:124
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: mutt_globals.h:50
static void collapse_all(struct Context *ctx, struct Menu *menu, int toggle)
Collapse/uncollapse all threads.
Definition: index.c:237
#define FREE(x)
Definition: memory.h:40
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1667
Mailbox has changed, NotifyMailbox, EventMailbox.
Definition: notify_type.h:47
#define MUTT_OPEN_NO_FLAGS
No flags are set.
Definition: mxapi.h:60
bool notify_observer_remove(struct Notify *notify, observer_t callback, void *global_data)
Remove an observer from an object.
Definition: notify.c:212
void ctx_free(struct Context **ptr)
Free a Context.
Definition: context.c:49
struct Notify * notify
Notifications: NotifyMailbox, EventMailbox.
Definition: mailbox.h:144
struct Mailbox * mailbox
Current Mailbox.
Definition: shared_data.h:41
struct Buffer pathbuf
Definition: mailbox.h:83
struct MuttWindow * win_index
Definition: lib.h:66
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
struct MuttWindow * dialog_find(struct MuttWindow *win)
Find the parent Dialog of a Window.
Definition: dialog.c:45
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_snc(), and mbox_close() ...
Definition: mxapi.h:75
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:78
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:156
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ change_folder_notmuch()

static struct Mailbox* change_folder_notmuch ( struct Menu menu,
char *  buf,
int  buflen,
int *  oldcount,
struct IndexSharedData shared,
bool  read_only 
)
static

Change to a different Notmuch Mailbox by string.

Parameters
menuCurrent Menu
bufFolder to change to
buflenLength of buffer
oldcountHow many items are currently in the index
sharedShared Index data
read_onlyOpen Mailbox in read-only mode

Definition at line 785 of file index.c.

788 {
789  if (!nm_url_from_query(NULL, buf, buflen))
790  {
791  mutt_message(_("Failed to create query, aborting"));
792  return NULL;
793  }
794 
795  struct Mailbox *m_query = mx_path_resolve(buf);
796  change_folder_mailbox(menu, m_query, oldcount, shared, read_only);
797  return m_query;
798 }
#define mutt_message(...)
Definition: logging.h:83
#define _(a)
Definition: message.h:28
static void change_folder_mailbox(struct Menu *menu, struct Mailbox *m, int *oldcount, struct IndexSharedData *shared, bool read_only)
Change to a different Mailbox by pointer.
Definition: index.c:664
A mailbox.
Definition: mailbox.h:81
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1667
char * nm_url_from_query(struct Mailbox *m, char *buf, size_t buflen)
Turn a query into a URL.
Definition: notmuch.c:1647
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ change_folder_string()

static void change_folder_string ( struct Menu menu,
char *  buf,
size_t  buflen,
int *  oldcount,
struct IndexSharedData shared,
bool *  pager_return,
bool  read_only 
)
static

Change to a different Mailbox by string.

Parameters
menuCurrent Menu
bufFolder to change to
buflenLength of buffer
oldcountHow many items are currently in the index
sharedShared Index data
pager_returnReturn to the pager afterwards
read_onlyOpen Mailbox in read-only mode

Definition at line 811 of file index.c.

814 {
815 #ifdef USE_NNTP
816  if (OptNews)
817  {
818  OptNews = false;
819  nntp_expand_path(buf, buflen, &CurrentNewsSrv->conn->account);
820  }
821  else
822 #endif
823  {
824  const char *const c_folder = cs_subset_string(shared->sub, "folder");
825  mx_path_canon(buf, buflen, c_folder, NULL);
826  }
827 
828  enum MailboxType type = mx_path_probe(buf);
829  if ((type == MUTT_MAILBOX_ERROR) || (type == MUTT_UNKNOWN))
830  {
831  // Look for a Mailbox by its description, before failing
832  struct Mailbox *m = mailbox_find_name(buf);
833  if (m)
834  {
835  change_folder_mailbox(menu, m, oldcount, shared, read_only);
836  *pager_return = false;
837  }
838  else
839  mutt_error(_("%s is not a mailbox"), buf);
840  return;
841  }
842 
843  /* past this point, we don't return to the pager on error */
844  *pager_return = false;
845 
846  struct Mailbox *m = mx_path_resolve(buf);
847  change_folder_mailbox(menu, m, oldcount, shared, read_only);
848 }
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:36
struct ConfigSubset * sub
Config set to use.
Definition: shared_data.h:38
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:47
Error occurred examining Mailbox.
Definition: mailbox.h:46
static void change_folder_mailbox(struct Menu *menu, struct Mailbox *m, int *oldcount, struct IndexSharedData *shared, bool read_only)
Change to a different Mailbox by pointer.
Definition: index.c:664
struct Mailbox * mailbox_find_name(const char *name)
Find the mailbox with a given name.
Definition: mailbox.c:140
A mailbox.
Definition: mailbox.h:81
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:295
struct Connection * conn
Definition: adata.h:63
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1316
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
#define mutt_error(...)
Definition: logging.h:84
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1667
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition: nntp.c:77
void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *acct)
Make fully qualified url from newsgroup name.
Definition: newsrc.c:559
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:1362
WHERE bool OptNews
(pseudo) used to change reader mode
Definition: options.h:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ 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 at line 853 of file index.c.

854 {
855  buf[0] = '\0';
856 
857  struct IndexSharedData *shared = menu->mdata;
858  struct Mailbox *m = shared->mailbox;
859 
860  if (!m || !menu || (line < 0) || (line >= m->email_max))
861  return;
862 
863  struct Email *e = mutt_get_virt_email(m, line);
864  if (!e)
865  return;
866 
868  struct MuttThread *tmp = NULL;
869 
870  const short c_sort = cs_subset_sort(shared->sub, "sort");
871  if (((c_sort & SORT_MASK) == SORT_THREADS) && e->tree)
872  {
873  flags |= MUTT_FORMAT_TREE; /* display the thread tree */
874  if (e->display_subject)
875  flags |= MUTT_FORMAT_FORCESUBJ;
876  else
877  {
878  const int reverse = c_sort & SORT_REVERSE;
879  int edgemsgno;
880  if (reverse)
881  {
882  if (menu->top + menu->pagelen > menu->max)
883  edgemsgno = m->v2r[menu->max - 1];
884  else
885  edgemsgno = m->v2r[menu->top + menu->pagelen - 1];
886  }
887  else
888  edgemsgno = m->v2r[menu->top];
889 
890  for (tmp = e->thread->parent; tmp; tmp = tmp->parent)
891  {
892  if (!tmp->message)
893  continue;
894 
895  /* if no ancestor is visible on current screen, provisionally force
896  * subject... */
897  if (reverse ? (tmp->message->msgno > edgemsgno) : (tmp->message->msgno < edgemsgno))
898  {
899  flags |= MUTT_FORMAT_FORCESUBJ;
900  break;
901  }
902  else if (tmp->message->vnum >= 0)
903  break;
904  }
905  if (flags & MUTT_FORMAT_FORCESUBJ)
906  {
907  for (tmp = e->thread->prev; tmp; tmp = tmp->prev)
908  {
909  if (!tmp->message)
910  continue;
911 
912  /* ...but if a previous sibling is available, don't force it */
913  if (reverse ? (tmp->message->msgno > edgemsgno) : (tmp->message->msgno < edgemsgno))
914  break;
915  else if (tmp->message->vnum >= 0)
916  {
917  flags &= ~MUTT_FORMAT_FORCESUBJ;
918  break;
919  }
920  }
921  }
922  }
923  }
924 
925  const char *const c_index_format =
926  cs_subset_string(shared->sub, "index_format");
927  mutt_make_string(buf, buflen, menu->win_index->state.cols, NONULL(c_index_format),
928  m, shared->ctx->msg_in_pager, e, flags, NULL);
929 }
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
#define NONULL(x)
Definition: string2.h:37
int msg_in_pager
Message currently shown in the pager.
Definition: context.h:43
The envelope/body of an email.
Definition: email.h:37
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:408
#define MUTT_FORMAT_FORCESUBJ
Print the subject even if unchanged.
Definition: format_flags.h:31
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
struct ConfigSubset * sub
Config set to use.
Definition: shared_data.h:38
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
bool display_subject
Used for threading.
Definition: email.h:57
Data shared between Index, Pager and Sidebar.
Definition: shared_data.h:36
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
#define MUTT_FORMAT_TREE
Draw the thread tree.
Definition: format_flags.h:32
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:1410
struct Context * ctx
Current Mailbox view.
Definition: shared_data.h:39
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:272
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:58
Sort by email threads.
Definition: sort2.h:49
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:122
void * mdata
Private data.
Definition: lib.h:129
int email_max
Number of pointers in emails.
Definition: mailbox.h:100
A mailbox.
Definition: mailbox.h:81
int top
Entry that is the top of the current page.
Definition: lib.h:78
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:295
int vnum
Virtual message number.
Definition: email.h:88
int pagelen
Number of entries per screen.
Definition: lib.h:63
char * tree
Character string to print thread tree.
Definition: email.h:94
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
int max
Number of entries in the menu.
Definition: lib.h:60
An Email conversation.
Definition: thread.h:34
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:101
#define MUTT_FORMAT_INDEX
This is a main index entry.
Definition: format_flags.h:36
struct Mailbox * mailbox
Current Mailbox.
Definition: shared_data.h:41
struct MuttWindow * win_index
Definition: lib.h:66
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:79
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:78
int msgno
Number displayed to the user.
Definition: email.h:87
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ index_color()

int index_color ( struct Menu menu,
int  line 
)

Calculate the colour for a line of the index - Implements Menu::color()

Definition at line 934 of file index.c.

935 {
936  struct IndexSharedData *shared = menu->mdata;
937  struct Mailbox *m = shared->mailbox;
938  if (!m || (line < 0))
939  return 0;
940 
941  struct Email *e = mutt_get_virt_email(m, line);
942  if (!e)
943  return 0;
944 
945  if (e->pair)
946  return e->pair;
947 
948  mutt_set_header_color(m, e);
949  return e->pair;
950 }
The envelope/body of an email.
Definition: email.h:37
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:408
Data shared between Index, Pager and Sidebar.
Definition: shared_data.h:36
void * mdata
Private data.
Definition: lib.h:129
A mailbox.
Definition: mailbox.h:81
void mutt_set_header_color(struct Mailbox *m, struct Email *e)
Select a colour for a message.
Definition: index.c:4197
struct Mailbox * mailbox
Current Mailbox.
Definition: shared_data.h:41
int pair
Color-pair to use when displaying in the index.
Definition: email.h:80
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_draw_statusline()

void mutt_draw_statusline ( int  cols,
const char *  buf,
size_t  buflen 
)

Draw a highlighted status bar.

Parameters
colsMaximum number of screen columns
bufMessage to be displayed
buflenLength of the buffer

Users configure the highlighting of the status bar, e.g. color status red default "[0-9][0-9]:[0-9][0-9]"

Where regexes overlap, the one nearest the start will be used. If two regexes start at the same place, the longer match will be used.

Definition at line 964 of file index.c.

965 {
966  if (!buf || !stdscr)
967  return;
968 
969  size_t i = 0;
970  size_t offset = 0;
971  bool found = false;
972  size_t chunks = 0;
973  size_t len = 0;
974 
975  struct StatusSyntax
976  {
977  int color;
978  int first;
979  int last;
980  } *syntax = NULL;
981 
982  do
983  {
984  struct ColorLine *cl = NULL;
985  found = false;
986 
987  if (!buf[offset])
988  break;
989 
990  /* loop through each "color status regex" */
991  STAILQ_FOREACH(cl, mutt_color_status_line(), entries)
992  {
993  regmatch_t pmatch[cl->match + 1];
994 
995  if (regexec(&cl->regex, buf + offset, cl->match + 1, pmatch, 0) != 0)
996  continue; /* regex doesn't match the status bar */
997 
998  int first = pmatch[cl->match].rm_so + offset;
999  int last = pmatch[cl->match].rm_eo + offset;
1000 
1001  if (first == last)
1002  continue; /* ignore an empty regex */
1003 
1004  if (!found)
1005  {
1006  chunks++;
1007  mutt_mem_realloc(&syntax, chunks * sizeof(struct StatusSyntax));
1008  }
1009 
1010  i = chunks - 1;
1011  if (!found || (first < syntax[i].first) ||
1012  ((first == syntax[i].first) && (last > syntax[i].last)))
1013  {
1014  syntax[i].color = cl->pair;
1015  syntax[i].first = first;
1016  syntax[i].last = last;
1017  }
1018  found = true;
1019  }
1020 
1021  if (syntax)
1022  {
1023  offset = syntax[i].last;
1024  }
1025  } while (found);
1026 
1027  /* Only 'len' bytes will fit into 'cols' screen columns */
1028  len = mutt_wstr_trunc(buf, buflen, cols, NULL);
1029 
1030  offset = 0;
1031 
1032  if ((chunks > 0) && (syntax[0].first > 0))
1033  {
1034  /* Text before the first highlight */
1035  mutt_window_addnstr(buf, MIN(len, syntax[0].first));
1036  attrset(mutt_color(MT_COLOR_STATUS));
1037  if (len <= syntax[0].first)
1038  goto dsl_finish; /* no more room */
1039 
1040  offset = syntax[0].first;
1041  }
1042 
1043  for (i = 0; i < chunks; i++)
1044  {
1045  /* Highlighted text */
1046  attrset(syntax[i].color);
1047  mutt_window_addnstr(buf + offset, MIN(len, syntax[i].last) - offset);
1048  if (len <= syntax[i].last)
1049  goto dsl_finish; /* no more room */
1050 
1051  size_t next;
1052  if ((i + 1) == chunks)
1053  {
1054  next = len;
1055  }
1056  else
1057  {
1058  next = MIN(len, syntax[i + 1].first);
1059  }
1060 
1061  attrset(mutt_color(MT_COLOR_STATUS));
1062  offset = syntax[i].last;
1063  mutt_window_addnstr(buf + offset, next - offset);
1064 
1065  offset = next;
1066  if (offset >= len)
1067  goto dsl_finish; /* no more room */
1068  }
1069 
1070  attrset(mutt_color(MT_COLOR_STATUS));
1071  if (offset < len)
1072  {
1073  /* Text after the last highlight */
1074  mutt_window_addnstr(buf + offset, len - offset);
1075  }
1076 
1077  int width = mutt_strwidth(buf);
1078  if (width < cols)
1079  {
1080  /* Pad the rest of the line with whitespace */
1081  mutt_paddstr(cols - width, "");
1082  }
1083 dsl_finish:
1084  FREE(&syntax);
1085 }
#define MIN(a, b)
Definition: memory.h:31
int pair
Colour pair index.
Definition: color.h:107
void mutt_paddstr(int n, const char *s)
Display a string on screen, padded if necessary.
Definition: curs_lib.c:1156
int match
Substring to match, 0 for old behaviour.
Definition: color.h:102
int mutt_window_addnstr(const char *str, int num)
Write a partial string to a Window.
Definition: mutt_window.c:501
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
Status bar (takes a pattern)
Definition: color.h:75
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1249
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:1199
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
int mutt_color(enum ColorId id)
Return the color of an object.
Definition: color.c:1393
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:84
regex_t regex
Compiled regex.
Definition: color.h:101
struct ColorLineList * mutt_color_status_line(void)
Return the ColorLineList for the status_line.
Definition: color.c:1402
#define FREE(x)
Definition: memory.h:40
A regular expression and a color to highlight a line.
Definition: color.h:99
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ index_custom_redraw()

static void index_custom_redraw ( struct Menu menu)
static

Redraw the index - Implements Menu::custom_redraw()

Definition at line 1090 of file index.c.

1091 {
1092  if (menu->redraw & REDRAW_FULL)
1093  {
1094  menu_redraw_full(menu);
1095  mutt_show_error();
1096  }
1097 
1098  struct IndexSharedData *shared = menu->mdata;
1099  struct Mailbox *m = shared->mailbox;
1100  const int index = menu_get_index(menu);
1101  if (m && m->emails && (index < m->vcount))
1102  {
1103  menu_check_recenter(menu);
1104 
1105  if (menu->redraw & REDRAW_INDEX)
1106  {
1107  menu_redraw_index(menu);
1109  }
1110  else if (menu->redraw & REDRAW_MOTION)
1111  menu_redraw_motion(menu);
1112  else if (menu->redraw & REDRAW_CURRENT)
1113  menu_redraw_current(menu);
1114  }
1115 
1116  if (menu->redraw & REDRAW_STATUS)
1117  {
1118  char buf[1024];
1119  const char *const c_status_format =
1120  cs_subset_string(shared->sub, "status_format");
1121  menu_status_line(buf, sizeof(buf), shared, menu, menu->win_ibar->state.cols,
1122  NONULL(c_status_format));
1123  mutt_window_move(menu->win_ibar, 0, 0);
1125  mutt_draw_statusline(menu->win_ibar->state.cols, buf, sizeof(buf));
1127  menu->redraw &= ~REDRAW_STATUS;
1128  const bool c_ts_enabled = cs_subset_bool(shared->sub, "ts_enabled");
1129  if (c_ts_enabled && TsSupported)
1130  {
1131  const char *const c_ts_status_format =
1132  cs_subset_string(shared->sub, "ts_status_format");
1133  menu_status_line(buf, sizeof(buf), shared, menu, sizeof(buf),
1134  NONULL(c_ts_status_format));
1135  mutt_ts_status(buf);
1136  const char *const c_ts_icon_format =
1137  cs_subset_string(shared->sub, "ts_icon_format");
1138  menu_status_line(buf, sizeof(buf), shared, menu, sizeof(buf), NONULL(c_ts_icon_format));
1139  mutt_ts_icon(buf);
1140  }
1141  }
1142 
1143  menu->redraw = REDRAW_NO_FLAGS;
1144 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
#define NONULL(x)
Definition: string2.h:37
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:71
void mutt_curses_set_color(enum ColorId color)
Set the current colour for text.
Definition: mutt_curses.c:56
struct ConfigSubset * sub
Config set to use.
Definition: shared_data.h:38
Data shared between Index, Pager and Sidebar.
Definition: shared_data.h:36
int vcount
The number of virtual messages.
Definition: mailbox.h:102
int mutt_window_move(struct MuttWindow *win, int col, int row)
Move the cursor in a Window.
Definition: mutt_window.c:382
struct MuttWindow * win_ibar
Definition: lib.h:67
Plain text.
Definition: color.h:58
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:58
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:122
void * mdata
Private data.
Definition: lib.h:129
Status bar (takes a pattern)
Definition: color.h:75
A mailbox.
Definition: mailbox.h:81
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:295
void menu_status_line(char *buf, size_t buflen, struct IndexSharedData *shared, struct Menu *menu, int cols, const char *fmt)
Create the status line.
Definition: status.c:430
bool TsSupported
Terminal Setting is supported.
Definition: terminal.c:43
MuttRedrawFlags redraw
When to redraw the screen.
Definition: lib.h:61
void mutt_ts_icon(char *str)
Set the icon in the terminal title bar.
Definition: terminal.c:118
void mutt_show_error(void)
Show the user an error message.
Definition: curs_lib.c:554
void mutt_ts_status(char *str)
Set the text of the terminal title bar.
Definition: terminal.c:104
struct Mailbox * mailbox
Current Mailbox.
Definition: shared_data.h:41
void mutt_draw_statusline(int cols, const char *buf, size_t buflen)
Draw a highlighted status bar.
Definition: index.c:964
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_index_menu()

struct Mailbox* mutt_index_menu ( struct MuttWindow dlg,
struct Mailbox m_init 
)

Display a list of emails.

Parameters
dlgDialog containing Windows to draw on
m_initInitial mailbox
Return values
Mailboxopen in the index

This function handles the message index window as well as commands returned from the pager (MENU_PAGER).

Definition at line 1155 of file index.c.

1156 {
1157  struct Context *ctx_old = Context;
1158  struct IndexSharedData *shared = dlg->wdata;
1159  index_shared_data_set_context(shared, ctx_new(m_init));
1160 
1161  struct MuttWindow *panel_index = mutt_window_find(dlg, WT_INDEX);
1162  struct MuttWindow *panel_pager = mutt_window_find(dlg, WT_PAGER);
1163 
1164  struct IndexPrivateData *priv = panel_index->wdata;
1165  priv->attach_msg = OptAttachMsg;
1166  priv->win_index = mutt_window_find(panel_index, WT_MENU);
1167  priv->win_ibar = mutt_window_find(panel_index, WT_INDEX_BAR);
1168  priv->win_pager = mutt_window_find(panel_pager, WT_MENU);
1169  priv->win_pbar = mutt_window_find(panel_pager, WT_PAGER_BAR);
1170 
1171  int op = OP_NULL;
1172 
1173 #ifdef USE_NNTP
1174  if (shared->mailbox && (shared->mailbox->type == MUTT_NNTP))
1175  dlg->help_data = IndexNewsHelp;
1176  else
1177 #endif
1178  dlg->help_data = IndexHelp;
1179  dlg->help_menu = MENU_MAIN;
1180 
1181  priv->menu = priv->win_index->wdata;
1182  priv->menu->win_ibar = priv->win_ibar;
1183  priv->menu->mdata = shared;
1184  priv->menu->make_entry = index_make_entry;
1185  priv->menu->color = index_color;
1187  menu_set_index(priv->menu, ci_first_message(shared->mailbox));
1188  mutt_window_reflow(NULL);
1189 
1190  if (!priv->attach_msg)
1191  {
1192  /* force the mailbox check after we enter the folder */
1194  }
1195 #ifdef USE_INOTIFY
1196  mutt_monitor_add(NULL);
1197 #endif
1198 
1199  {
1200  const short c_sort = cs_subset_sort(shared->sub, "sort");
1201  const bool c_collapse_all = cs_subset_bool(shared->sub, "collapse_all");
1202  if (((c_sort & SORT_MASK) == SORT_THREADS) && c_collapse_all)
1203  {
1204  collapse_all(shared->ctx, priv->menu, 0);
1206  }
1207  }
1208 
1209  while (true)
1210  {
1211  /* Clear the tag prefix unless we just started it. Don't clear
1212  * the prefix on a timeout (op==-2), but do clear on an abort (op==-1) */
1213  if (priv->tag && (op != OP_TAG_PREFIX) && (op != OP_TAG_PREFIX_COND) && (op != -2))
1214  priv->tag = false;
1215 
1216  /* check if we need to resort the index because just about
1217  * any 'op' below could do mutt_enter_command(), either here or
1218  * from any new priv->menu launched, and change $sort/$sort_aux */
1219  if (OptNeedResort && shared->mailbox && (shared->mailbox->msg_count != 0) &&
1220  (menu_get_index(priv->menu) >= 0))
1221  {
1222  resort_index(shared->ctx, priv->menu);
1223  }
1224 
1225  priv->menu->max = shared->mailbox ? shared->mailbox->vcount : 0;
1226  priv->oldcount = shared->mailbox ? shared->mailbox->msg_count : 0;
1227 
1228  {
1229  const short c_sort = cs_subset_sort(shared->sub, "sort");
1230  if (OptRedrawTree && shared->mailbox && (shared->mailbox->msg_count != 0) &&
1231  ((c_sort & SORT_MASK) == SORT_THREADS))
1232  {
1233  mutt_draw_tree(shared->ctx->threads);
1235  OptRedrawTree = false;
1236  }
1237  }
1238 
1239  if (shared->mailbox)
1240  {
1241  mailbox_gc_run();
1242 
1243  shared->ctx->menu = priv->menu;
1244  /* check for new mail in the mailbox. If nonzero, then something has
1245  * changed about the file (either we got new mail or the file was
1246  * modified underneath us.) */
1247  enum MxStatus check = mx_mbox_check(shared->mailbox);
1248 
1249  if (check == MX_STATUS_ERROR)
1250  {
1251  if (mutt_buffer_is_empty(&shared->mailbox->pathbuf))
1252  {
1253  /* fatal error occurred */
1254  struct Context *ctx = shared->ctx;
1255  index_shared_data_set_context(shared, NULL);
1256  ctx_free(&ctx);
1258  }
1259 
1260  OptSearchInvalid = true;
1261  }
1262  else if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED) ||
1263  (check == MX_STATUS_FLAGS))
1264  {
1265  /* notify the user of new mail */
1266  if (check == MX_STATUS_REOPENED)
1267  {
1268  mutt_error(
1269  _("Mailbox was externally modified. Flags may be wrong."));
1270  }
1271  else if (check == MX_STATUS_NEW_MAIL)
1272  {
1273  for (size_t i = 0; i < shared->mailbox->msg_count; i++)
1274  {
1275  const struct Email *e = shared->mailbox->emails[i];
1276  if (e && !e->read && !e->old)
1277  {
1278  mutt_message(_("New mail in this mailbox"));
1279  const bool c_beep_new = cs_subset_bool(shared->sub, "beep_new");
1280  if (c_beep_new)
1281  mutt_beep(true);
1282  const char *const c_new_mail_command =
1283  cs_subset_string(shared->sub, "new_mail_command");
1284  if (c_new_mail_command)
1285  {
1286  char cmd[1024];
1287  menu_status_line(cmd, sizeof(cmd), shared, priv->menu,
1288  sizeof(cmd), NONULL(c_new_mail_command));
1289  if (mutt_system(cmd) != 0)
1290  mutt_error(_("Error running \"%s\""), cmd);
1291  }
1292  break;
1293  }
1294  }
1295  }
1296  else if (check == MX_STATUS_FLAGS)
1297  {
1298  mutt_message(_("Mailbox was externally modified"));
1299  }
1300 
1301  /* avoid the message being overwritten by mailbox */
1302  priv->do_mailbox_notify = false;
1303 
1304  bool verbose = shared->mailbox->verbose;
1305  shared->mailbox->verbose = false;
1306  update_index(priv->menu, shared->ctx, check, priv->oldcount, shared);
1307  shared->mailbox->verbose = verbose;
1308  priv->menu->max = shared->mailbox->vcount;
1310  OptSearchInvalid = true;
1311  }
1312 
1313  if (shared->mailbox)
1314  {
1316  shared, mutt_get_virt_email(shared->mailbox, menu_get_index(priv->menu)));
1317  }
1318  }
1319 
1320  if (!priv->attach_msg)
1321  {
1322  /* check for new mail in the incoming folders */
1323  priv->oldcount = priv->newcount;
1324  priv->newcount = mutt_mailbox_check(shared->mailbox, 0);
1325  if (priv->newcount != priv->oldcount)
1327  if (priv->do_mailbox_notify)
1328  {
1329  if (mutt_mailbox_notify(shared->mailbox))
1330  {
1332  const bool c_beep_new = cs_subset_bool(shared->sub, "beep_new");
1333  if (c_beep_new)
1334  mutt_beep(true);
1335  const char *const c_new_mail_command =
1336  cs_subset_string(shared->sub, "new_mail_command");
1337  if (c_new_mail_command)
1338  {
1339  char cmd[1024];
1340  menu_status_line(cmd, sizeof(cmd), shared, priv->menu, sizeof(cmd),
1341  NONULL(c_new_mail_command));
1342  if (mutt_system(cmd) != 0)
1343  mutt_error(_("Error running \"%s\""), cmd);
1344  }
1345  }
1346  }
1347  else
1348  priv->do_mailbox_notify = true;
1349  }
1350 
1351  if (op >= 0)
1353 
1354  if (priv->in_pager)
1355  {
1356  mutt_curses_set_cursor(MUTT_CURSOR_VISIBLE); /* fallback from the pager */
1357  }
1358  else
1359  {
1360  index_custom_redraw(priv->menu);
1361  window_redraw(RootWindow, false);
1362 
1363  /* give visual indication that the next command is a tag- command */
1364  if (priv->tag)
1365  {
1366  mutt_window_mvaddstr(MessageWindow, 0, 0, "tag-");
1368  }
1369 
1370  const bool c_arrow_cursor = cs_subset_bool(shared->sub, "arrow_cursor");
1371  const bool c_braille_friendly =
1372  cs_subset_bool(shared->sub, "braille_friendly");
1373  const int index = menu_get_index(priv->menu);
1374  if (c_arrow_cursor)
1375  {
1376  mutt_window_move(priv->menu->win_index, 2, index - priv->menu->top);
1377  }
1378  else if (c_braille_friendly)
1379  {
1380  mutt_window_move(priv->menu->win_index, 0, index - priv->menu->top);
1381  }
1382  else
1383  {
1384  mutt_window_move(priv->menu->win_index, priv->menu->win_index->state.cols - 1,
1385  index - priv->menu->top);
1386  }
1387  mutt_refresh();
1388 
1389  if (SigWinch)
1390  {
1391  SigWinch = 0;
1393  priv->menu->top = 0; /* so we scroll the right amount */
1394  /* force a real complete redraw. clrtobot() doesn't seem to be able
1395  * to handle every case without this. */
1396  clearok(stdscr, true);
1398  continue;
1399  }
1400 
1401  op = km_dokey(MENU_MAIN);
1402 
1403  /* either user abort or timeout */
1404  if (op < 0)
1405  {
1407  if (priv->tag)
1409  continue;
1410  }
1411 
1412  mutt_debug(LL_DEBUG1, "Got op %s (%d)\n", OpStrings[op][0], op);
1413 
1415 
1416  /* special handling for the priv->tag-prefix function */
1417  const bool c_auto_tag = cs_subset_bool(shared->sub, "auto_tag");
1418  if ((op == OP_TAG_PREFIX) || (op == OP_TAG_PREFIX_COND))
1419  {
1420  /* A second priv->tag-prefix command aborts */
1421  if (priv->tag)
1422  {
1423  priv->tag = false;
1425  continue;
1426  }
1427 
1428  if (!shared->mailbox)
1429  {
1430  mutt_error(_("No mailbox is open"));
1431  continue;
1432  }
1433 
1434  if (shared->mailbox->msg_tagged == 0)
1435  {
1436  if (op == OP_TAG_PREFIX)
1437  mutt_error(_("No tagged messages"));
1438  else if (op == OP_TAG_PREFIX_COND)
1439  {
1441  mutt_message(_("Nothing to do"));
1442  }
1443  continue;
1444  }
1445 
1446  /* get the real command */
1447  priv->tag = true;
1448  continue;
1449  }
1450  else if (c_auto_tag && shared->mailbox && (shared->mailbox->msg_tagged != 0))
1451  {
1452  priv->tag = true;
1453  }
1454 
1455  mutt_clear_error();
1456  }
1457 
1458 #ifdef USE_NNTP
1459  OptNews = false; /* for any case */
1460 #endif
1461 
1462 #ifdef USE_NOTMUCH
1463  nm_db_debug_check(shared->mailbox);
1464 #endif
1465 
1466  switch (op)
1467  {
1468  /* ----------------------------------------------------------------------
1469  * movement commands
1470  */
1471 
1472  case OP_BOTTOM_PAGE:
1473  menu_bottom_page(priv->menu);
1474  break;
1475  case OP_CURRENT_BOTTOM:
1476  menu_current_bottom(priv->menu);
1477  break;
1478  case OP_CURRENT_MIDDLE:
1479  menu_current_middle(priv->menu);
1480  break;
1481  case OP_CURRENT_TOP:
1482  menu_current_top(priv->menu);
1483  break;
1484  case OP_FIRST_ENTRY:
1485  menu_first_entry(priv->menu);
1486  break;
1487  case OP_HALF_DOWN:
1488  menu_half_down(priv->menu);
1489  break;
1490  case OP_HALF_UP:
1491  menu_half_up(priv->menu);
1492  break;
1493  case OP_LAST_ENTRY:
1494  menu_last_entry(priv->menu);
1495  break;
1496  case OP_MIDDLE_PAGE:
1497  menu_middle_page(priv->menu);
1498  break;
1499  case OP_NEXT_LINE:
1500  menu_next_line(priv->menu);
1501  break;
1502  case OP_NEXT_PAGE:
1503  menu_next_page(priv->menu);
1504  break;
1505  case OP_PREV_LINE:
1506  menu_prev_line(priv->menu);
1507  break;
1508  case OP_PREV_PAGE:
1509  menu_prev_page(priv->menu);
1510  break;
1511  case OP_TOP_PAGE:
1512  menu_top_page(priv->menu);
1513  break;
1514 
1515 #ifdef USE_NNTP
1516  case OP_GET_PARENT:
1517  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
1518  break;
1519  /* fallthrough */
1520 
1521  case OP_GET_MESSAGE:
1522  {
1523  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_READONLY | CHECK_ATTACH))
1524  break;
1525  char buf[PATH_MAX] = { 0 };
1526  if (shared->mailbox->type == MUTT_NNTP)
1527  {
1528  if (op == OP_GET_MESSAGE)
1529  {
1530  if ((mutt_get_field(_("Enter Message-Id: "), buf, sizeof(buf),
1531  MUTT_COMP_NO_FLAGS, false, NULL, NULL) != 0) ||
1532  (buf[0] == '\0'))
1533  {
1534  break;
1535  }
1536  }
1537  else
1538  {
1539  if (!shared->email || STAILQ_EMPTY(&shared->email->env->references))
1540  {
1541  mutt_error(_("Article has no parent reference"));
1542  break;
1543  }
1544  mutt_str_copy(buf, STAILQ_FIRST(&shared->email->env->references)->data,
1545  sizeof(buf));
1546  }
1547  if (!shared->mailbox->id_hash)
1548  shared->mailbox->id_hash = mutt_make_id_hash(shared->mailbox);
1549  struct Email *e = mutt_hash_find(shared->mailbox->id_hash, buf);
1550  if (e)
1551  {
1552  if (e->vnum != -1)
1553  {
1554  menu_set_index(priv->menu, e->vnum);
1555  }
1556  else if (e->collapsed)
1557  {
1559  mutt_set_vnum(shared->mailbox);
1560  menu_set_index(priv->menu, e->vnum);
1561  }
1562  else
1563  mutt_error(_("Message is not visible in limited view"));
1564  }
1565  else
1566  {
1567  mutt_message(_("Fetching %s from server..."), buf);
1568  int rc = nntp_check_msgid(shared->mailbox, buf);
1569  if (rc == 0)
1570  {
1571  e = shared->mailbox->emails[shared->mailbox->msg_count - 1];
1572  mutt_sort_headers(shared->mailbox, shared->ctx->threads, false,
1573  &shared->ctx->vsize);
1574  menu_set_index(priv->menu, e->vnum);
1576  }
1577  else if (rc > 0)
1578  mutt_error(_("Article %s not found on the server"), buf);
1579  }
1580  }
1581  break;
1582  }
1583 
1584  case OP_GET_CHILDREN:
1585  case OP_RECONSTRUCT_THREAD:
1586  {
1587  if (!prereq(shared->ctx, priv->menu,
1589  {
1590  break;
1591  }
1592  if (shared->mailbox->type != MUTT_NNTP)
1593  break;
1594 
1595  if (!shared->email)
1596  break;
1597 
1598  char buf[PATH_MAX] = { 0 };
1599  int oldmsgcount = shared->mailbox->msg_count;
1600  int oldindex = shared->email->index;
1601  int rc = 0;
1602 
1603  if (!shared->email->env->message_id)
1604  {
1605  mutt_error(_("No Message-Id. Unable to perform operation."));
1606  break;
1607  }
1608 
1609  mutt_message(_("Fetching message headers..."));
1610  if (!shared->mailbox->id_hash)
1611  shared->mailbox->id_hash = mutt_make_id_hash(shared->mailbox);
1612  mutt_str_copy(buf, shared->email->env->message_id, sizeof(buf));
1613 
1614  /* trying to find msgid of the root message */
1615  if (op == OP_RECONSTRUCT_THREAD)
1616  {
1617  struct ListNode *ref = NULL;
1618  STAILQ_FOREACH(ref, &shared->email->env->references, entries)
1619  {
1620  if (!mutt_hash_find(shared->mailbox->id_hash, ref->data))
1621  {
1622  rc = nntp_check_msgid(shared->mailbox, ref->data);
1623  if (rc < 0)
1624  break;
1625  }
1626 
1627  /* the last msgid in References is the root message */
1628  if (!STAILQ_NEXT(ref, entries))
1629  mutt_str_copy(buf, ref->data, sizeof(buf));
1630  }
1631  }
1632 
1633  /* fetching all child messages */
1634  if (rc >= 0)
1635  rc = nntp_check_children(shared->mailbox, buf);
1636 
1637  /* at least one message has been loaded */
1638  if (shared->mailbox->msg_count > oldmsgcount)
1639  {
1640  const int index = menu_get_index(priv->menu);
1641  struct Email *e_oldcur = mutt_get_virt_email(shared->mailbox, index);
1642  bool verbose = shared->mailbox->verbose;
1643 
1644  if (rc < 0)
1645  shared->mailbox->verbose = false;
1646  mutt_sort_headers(shared->mailbox, shared->ctx->threads,
1647  (op == OP_RECONSTRUCT_THREAD), &shared->ctx->vsize);
1648  shared->mailbox->verbose = verbose;
1649 
1650  /* Similar to OP_MAIN_ENTIRE_THREAD, keep displaying the old message, but
1651  * update the index */
1652  if (priv->in_pager)
1653  {
1654  menu_set_index(priv->menu, e_oldcur->vnum);
1656  op = OP_DISPLAY_MESSAGE;
1657  continue;
1658  }
1659 
1660  /* if the root message was retrieved, move to it */
1661  struct Email *e = mutt_hash_find(shared->mailbox->id_hash, buf);
1662  if (e)
1663  menu_set_index(priv->menu, e->vnum);
1664  else
1665  {
1666  /* try to restore old position */
1667  for (int i = 0; i < shared->mailbox->msg_count; i++)
1668  {
1669  e = shared->mailbox->emails[i];
1670  if (!e)
1671  break;
1672  if (e->index == oldindex)
1673  {
1674  menu_set_index(priv->menu, e->vnum);
1675  /* as an added courtesy, recenter the priv->menu
1676  * with the current entry at the middle of the screen */
1677  menu_check_recenter(priv->menu);
1678  menu_current_middle(priv->menu);
1679  }
1680  }
1681  }
1683  }
1684  else if (rc >= 0)
1685  {
1686  mutt_error(_("No deleted messages found in the thread"));
1687  /* Similar to OP_MAIN_ENTIRE_THREAD, keep displaying the old message, but
1688  * update the index */
1689  if (priv->in_pager)
1690  {
1691  op = OP_DISPLAY_MESSAGE;
1692  continue;
1693  }
1694  }
1695  break;
1696  }
1697 #endif
1698 
1699  case OP_JUMP:
1700  {
1701  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
1702  break;
1703  char buf[PATH_MAX] = { 0 };
1704  int msg_num = 0;
1705  if (isdigit(LastKey))
1707  if ((mutt_get_field(_("Jump to message: "), buf, sizeof(buf),
1708  MUTT_COMP_NO_FLAGS, false, NULL, NULL) != 0) ||
1709  (buf[0] == '\0'))
1710  {
1711  mutt_error(_("Nothing to do"));
1712  }
1713  else if (mutt_str_atoi(buf, &msg_num) < 0)
1714  mutt_error(_("Argument must be a message number"));
1715  else if ((msg_num < 1) || (msg_num > shared->mailbox->msg_count))
1716  mutt_error(_("Invalid message number"));
1717  else if (!shared->mailbox->emails[msg_num - 1]->visible)
1718  mutt_error(_("That message is not visible"));
1719  else
1720  {
1721  struct Email *e = shared->mailbox->emails[msg_num - 1];
1722 
1723  if (mutt_messages_in_thread(shared->mailbox, e, MIT_POSITION) > 1)
1724  {
1726  mutt_set_vnum(shared->mailbox);
1727  }
1728  menu_set_index(priv->menu, e->vnum);
1729  }
1730 
1731  if (priv->in_pager)
1732  {
1733  op = OP_DISPLAY_MESSAGE;
1734  continue;
1735  }
1736  else
1738 
1739  break;
1740  }
1741 
1742  /* --------------------------------------------------------------------
1743  * 'index' specific commands
1744  */
1745 
1746  case OP_MAIN_DELETE_PATTERN:
1747  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_READONLY | CHECK_ATTACH))
1748  {
1749  break;
1750  }
1751  /* L10N: CHECK_ACL */
1752  /* L10N: Due to the implementation details we do not know whether we
1753  delete zero, 1, 12, ... messages. So in English we use
1754  "messages". Your language might have other means to express this. */
1755  if (!check_acl(shared->mailbox, MUTT_ACL_DELETE, _("Can't delete messages")))
1756  break;
1757 
1758  mutt_pattern_func(shared->ctx, MUTT_DELETE, _("Delete messages matching: "));
1760  break;
1761 
1762 #ifdef USE_POP
1763  case OP_MAIN_FETCH_MAIL:
1764  if (!prereq(shared->ctx, priv->menu, CHECK_ATTACH))
1765  break;
1766  pop_fetch_mail();
1768  break;
1769 #endif /* USE_POP */
1770 
1771  case OP_SHOW_LOG_MESSAGES:
1772  {
1773 #ifdef USE_DEBUG_GRAPHVIZ
1774  dump_graphviz("index", shared->ctx);
1775 #endif
1776  char tempfile[PATH_MAX];
1777  mutt_mktemp(tempfile, sizeof(tempfile));
1778 
1779  FILE *fp = mutt_file_fopen(tempfile, "a+");
1780  if (!fp)
1781  {
1782  mutt_perror("fopen");
1783  break;
1784  }
1785 
1786  log_queue_save(fp);
1787  mutt_file_fclose(&fp);
1788 
1789  struct PagerData pdata = { 0 };
1790  struct PagerView pview = { &pdata };
1791 
1792  pdata.fname = tempfile;
1793 
1794  pview.banner = "messages";
1796  pview.mode = PAGER_MODE_OTHER;
1797 
1798  mutt_do_pager(&pview);
1799  break;
1800  }
1801 
1802  case OP_HELP:
1805  break;
1806 
1807  case OP_MAIN_SHOW_LIMIT:
1808  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
1809  break;
1810  if (!ctx_has_limit(shared->ctx))
1811  mutt_message(_("No limit pattern is in effect"));
1812  else
1813  {
1814  char buf2[256];
1815  /* L10N: ask for a limit to apply */
1816  snprintf(buf2, sizeof(buf2), _("Limit: %s"), shared->ctx->pattern);
1817  mutt_message("%s", buf2);
1818  }
1819  break;
1820 
1821  case OP_LIMIT_CURRENT_THREAD:
1822  case OP_MAIN_LIMIT:
1823  case OP_TOGGLE_READ:
1824  {
1825  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
1826  break;
1827  const bool lmt = ctx_has_limit(shared->ctx);
1828  int old_index = shared->email ? shared->email->index : -1;
1829  if (op == OP_TOGGLE_READ)
1830  {
1831  char buf2[1024];
1832 
1833  if (!lmt || !mutt_strn_equal(shared->ctx->pattern, "!~R!~D~s", 8))
1834  {
1835  snprintf(buf2, sizeof(buf2), "!~R!~D~s%s", lmt ? shared->ctx->pattern : ".*");
1836  }
1837  else
1838  {
1839  mutt_str_copy(buf2, shared->ctx->pattern + 8, sizeof(buf2));
1840  if ((*buf2 == '\0') || mutt_strn_equal(buf2, ".*", 2))
1841  snprintf(buf2, sizeof(buf2), "~A");
1842  }
1843  mutt_str_replace(&shared->ctx->pattern, buf2);
1844  mutt_pattern_func(shared->ctx, MUTT_LIMIT, NULL);
1845  }
1846 
1847  if (((op == OP_LIMIT_CURRENT_THREAD) &&
1848  mutt_limit_current_thread(shared->ctx, shared->email)) ||
1849  (op == OP_TOGGLE_READ) ||
1850  ((op == OP_MAIN_LIMIT) &&
1851  (mutt_pattern_func(shared->ctx, MUTT_LIMIT, _("Limit to messages matching: ")) == 0)))
1852  {
1853  int index = 0;
1854  if (old_index >= 0)
1855  {
1856  /* try to find what used to be the current message */
1857  for (size_t i = 0; i < shared->mailbox->vcount; i++)
1858  {
1859  struct Email *e = mutt_get_virt_email(shared->mailbox, i);
1860  if (!e)
1861  continue;
1862  if (e->index == old_index)
1863  {
1864  index = i;
1865  break;
1866  }
1867  }
1868  }
1869  menu_set_index(priv->menu, index);
1870 
1871  const short c_sort = cs_subset_sort(shared->sub, "sort");
1872  if ((shared->mailbox->msg_count != 0) && ((c_sort & SORT_MASK) == SORT_THREADS))
1873  {
1874  const bool c_collapse_all =
1875  cs_subset_bool(shared->sub, "collapse_all");
1876  if (c_collapse_all)
1877  collapse_all(shared->ctx, priv->menu, 0);
1878  mutt_draw_tree(shared->ctx->threads);
1879  }
1881  }
1882  if (lmt)
1883  mutt_message(_("To view all messages, limit to \"all\""));
1884  break;
1885  }
1886 
1887  case OP_QUIT:
1888  {
1889  if (priv->attach_msg)
1890  {
1891  priv->done = true;
1892  break;
1893  }
1894 
1895  const enum QuadOption c_quit = cs_subset_quad(shared->sub, "quit");
1896  if (query_quadoption(c_quit, _("Quit NeoMutt?")) == MUTT_YES)
1897  {
1898  priv->oldcount = shared->mailbox ? shared->mailbox->msg_count : 0;
1899 
1902 
1903  enum MxStatus check = MX_STATUS_OK;
1904  if (!shared->ctx || ((check = mx_mbox_close(shared->mailbox)) == MX_STATUS_OK))
1905  {
1906  struct Context *ctx = shared->ctx;
1907  index_shared_data_set_context(shared, NULL);
1908  ctx_free(&ctx);
1909  priv->done = true;
1910  }
1911  else
1912  {
1913  if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
1914  {
1915  update_index(priv->menu, shared->ctx, check, priv->oldcount, shared);
1916  }
1917 
1918  menu_queue_redraw(priv->menu, REDRAW_FULL); /* new mail arrived? */
1919  OptSearchInvalid = true;
1920  }
1921  }
1922  break;
1923  }
1924 
1925  case OP_REDRAW:
1926  mutt_window_reflow(NULL);
1927  clearok(stdscr, true);
1929  break;
1930 
1931  // Initiating a search can happen on an empty mailbox, but
1932  // searching for next/previous/... needs to be on a message and
1933  // thus a non-empty mailbox
1934  case OP_SEARCH_REVERSE:
1935  case OP_SEARCH_NEXT:
1936  case OP_SEARCH_OPPOSITE:
1937  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
1938  break;
1939  /* fallthrough */
1940  case OP_SEARCH:
1941  {
1942  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
1943  break;
1944  int index = menu_get_index(priv->menu);
1945  index = mutt_search_command(shared->mailbox, priv->menu, index, op);
1946  if (index != -1)
1947  menu_set_index(priv->menu, index);
1948  else
1950  break;
1951  }
1952 
1953  case OP_SORT:
1954  case OP_SORT_REVERSE:
1955  if (!mutt_select_sort(op == OP_SORT_REVERSE))
1956  break;
1957 
1958  if (shared->mailbox && (shared->mailbox->msg_count != 0))
1959  {
1960  resort_index(shared->ctx, priv->menu);
1961  OptSearchInvalid = true;
1962  }
1963  if (priv->in_pager)
1964  {
1965  op = OP_DISPLAY_MESSAGE;
1966  continue;
1967  }
1969  break;
1970 
1971  case OP_TAG:
1972  {
1973  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
1974  break;
1975  const bool c_auto_tag = cs_subset_bool(shared->sub, "auto_tag");
1976  if (priv->tag && !c_auto_tag)
1977  {
1978  struct Mailbox *m = shared->mailbox;
1979  for (size_t i = 0; i < m->msg_count; i++)
1980  {
1981  struct Email *e = m->emails[i];
1982  if (!e)
1983  break;
1984  if (e->visible)
1985  mutt_set_flag(m, e, MUTT_TAG, false);
1986  }
1988  }
1989  else
1990  {
1991  if (!shared->email)
1992  break;
1993  mutt_set_flag(shared->mailbox, shared->email, MUTT_TAG, !shared->email->tagged);
1994 
1996  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
1997  const int index = menu_get_index(priv->menu) + 1;
1998  if (c_resolve && (index < shared->mailbox->vcount))
1999  {
2000  menu_set_index(priv->menu, index);
2001  }
2002  else
2004  }
2005  break;
2006  }
2007 
2008  case OP_MAIN_TAG_PATTERN:
2009  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
2010  break;
2011  mutt_pattern_func(shared->ctx, MUTT_TAG, _("Tag messages matching: "));
2013  break;
2014 
2015  case OP_MAIN_UNDELETE_PATTERN:
2016  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_READONLY))
2017  break;
2018  /* L10N: CHECK_ACL */
2019  /* L10N: Due to the implementation details we do not know whether we
2020  undelete zero, 1, 12, ... messages. So in English we use
2021  "messages". Your language might have other means to express this. */
2022  if (!check_acl(shared->mailbox, MUTT_ACL_DELETE, _("Can't undelete messages")))
2023  break;
2024 
2025  if (mutt_pattern_func(shared->ctx, MUTT_UNDELETE,
2026  _("Undelete messages matching: ")) == 0)
2027  {
2029  }
2030  break;
2031 
2032  case OP_MAIN_UNTAG_PATTERN:
2033  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
2034  break;
2035  if (mutt_pattern_func(shared->ctx, MUTT_UNTAG, _("Untag messages matching: ")) == 0)
2037  break;
2038 
2039  case OP_COMPOSE_TO_SENDER:
2040  {
2041  if (!prereq(shared->ctx, priv->menu,
2043  break;
2044  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2045  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
2046  mutt_send_message(SEND_TO_SENDER, NULL, NULL, shared->mailbox, &el,
2047  shared->sub);
2048  emaillist_clear(&el);
2050  break;
2051  }
2052 
2053  /* --------------------------------------------------------------------
2054  * The following operations can be performed inside of the pager.
2055  */
2056 
2057 #ifdef USE_IMAP
2058  case OP_MAIN_IMAP_FETCH:
2059  if (shared->mailbox && (shared->mailbox->type == MUTT_IMAP))
2060  imap_check_mailbox(shared->mailbox, true);
2061  break;
2062 
2063  case OP_MAIN_IMAP_LOGOUT_ALL:
2064  if (shared->mailbox && (shared->mailbox->type == MUTT_IMAP))
2065  {
2066  const enum MxStatus check = mx_mbox_close(shared->mailbox);
2067  if (check == MX_STATUS_OK)
2068  {
2069  struct Context *ctx = shared->ctx;
2070  index_shared_data_set_context(shared, NULL);
2071  ctx_free(&ctx);
2072  }
2073  else
2074  {
2075  if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
2076  {
2077  update_index(priv->menu, shared->ctx, check, priv->oldcount, shared);
2078  }
2079  OptSearchInvalid = true;
2081  break;
2082  }
2083  }
2084  imap_logout_all();
2085  mutt_message(_("Logged out of IMAP servers"));
2086  OptSearchInvalid = true;
2088  break;
2089 #endif
2090 
2091  case OP_MAIN_SYNC_FOLDER:
2092  if (!shared->mailbox || (shared->mailbox->msg_count == 0))
2093  break;
2094 
2095  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_READONLY))
2096  break;
2097  {
2098  int ovc = shared->mailbox->vcount;
2099  int oc = shared->mailbox->msg_count;
2100  struct Email *e = NULL;
2101 
2102  /* don't attempt to move the cursor if there are no visible messages in the current limit */
2103  int index = menu_get_index(priv->menu);
2104  if (index < shared->mailbox->vcount)
2105  {
2106  /* threads may be reordered, so figure out what header the cursor
2107  * should be on. */
2108  int newidx = index;
2109  if (!shared->email)
2110  break;
2111  if (shared->email->deleted)
2112  newidx = ci_next_undeleted(shared->mailbox, index);
2113  if (newidx < 0)
2114  newidx = ci_previous_undeleted(shared->mailbox, index);
2115  if (newidx >= 0)
2116  e = mutt_get_virt_email(shared->mailbox, newidx);
2117  }
2118 
2119  enum MxStatus check = mx_mbox_sync(shared->mailbox);
2120  if (check == MX_STATUS_OK)
2121  {
2122  if (e && (shared->mailbox->vcount != ovc))
2123  {
2124  for (size_t i = 0; i < shared->mailbox->vcount; i++)
2125  {
2126  struct Email *e2 = mutt_get_virt_email(shared->mailbox, i);
2127  if (e2 == e)
2128  {
2129  menu_set_index(priv->menu, i);
2130  break;
2131  }
2132  }
2133  }
2134  OptSearchInvalid = true;
2135  }
2136  else if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
2137  {
2138  update_index(priv->menu, shared->ctx, check, oc, shared);
2139  }
2140 
2141  /* do a sanity check even if mx_mbox_sync failed. */
2142 
2143  index = menu_get_index(priv->menu);
2144  if ((index < 0) || (shared->mailbox && (index >= shared->mailbox->vcount)))
2145  {
2146  menu_set_index(priv->menu, ci_first_message(shared->mailbox));
2147  }
2148  }
2149 
2150  /* check for a fatal error, or all messages deleted */
2151  if (shared->mailbox && mutt_buffer_is_empty(&shared->mailbox->pathbuf))
2152  {
2153  struct Context *ctx = shared->ctx;
2154  index_shared_data_set_context(shared, NULL);
2155  ctx_free(&ctx);
2156  }
2157 
2158  /* if we were in the pager, redisplay the message */
2159  if (priv->in_pager)
2160  {
2161  op = OP_DISPLAY_MESSAGE;
2162  continue;
2163  }
2164  else
2166  break;
2167 
2168  case OP_MAIN_QUASI_DELETE:
2169  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2170  break;
2171  if (priv->tag)
2172  {
2173  struct Mailbox *m = shared->mailbox;
2174  for (size_t i = 0; i < m->msg_count; i++)
2175  {
2176  struct Email *e = m->emails[i];
2177  if (!e)
2178  break;
2179  if (message_is_tagged(e))
2180  {
2181  e->quasi_deleted = true;
2182  m->changed = true;
2183  }
2184  }
2185  }
2186  else
2187  {
2188  if (!shared->email)
2189  break;
2190  shared->email->quasi_deleted = true;
2191  shared->mailbox->changed = true;
2192  }
2193  break;
2194 
2195 #ifdef USE_NOTMUCH
2196  case OP_MAIN_ENTIRE_THREAD:
2197  {
2198  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2199  break;
2200  char buf[PATH_MAX] = { 0 };
2201  if (shared->mailbox->type != MUTT_NOTMUCH)
2202  {
2203  if (((shared->mailbox->type != MUTT_MH) && (shared->mailbox->type != MUTT_MAILDIR)) ||
2204  (!shared->email || !shared->email->env || !shared->email->env->message_id))
2205  {
2206  mutt_message(_("No virtual folder and no Message-Id, aborting"));
2207  break;
2208  } // no virtual folder, but we have message-id, reconstruct thread on-the-fly
2209  strncpy(buf, "id:", sizeof(buf));
2210  int msg_id_offset = 0;
2211  if ((shared->email->env->message_id)[0] == '<')
2212  msg_id_offset = 1;
2213  mutt_str_cat(buf, sizeof(buf), (shared->email->env->message_id) + msg_id_offset);
2214  if (buf[strlen(buf) - 1] == '>')
2215  buf[strlen(buf) - 1] = '\0';
2216 
2217  change_folder_notmuch(priv->menu, buf, sizeof(buf), &priv->oldcount, shared, false);
2218 
2219  // If notmuch doesn't contain the message, we're left in an empty
2220  // vfolder. No messages are found, but nm_read_entire_thread assumes
2221  // a valid message-id and will throw a segfault.
2222  //
2223  // To prevent that, stay in the empty vfolder and print an error.
2224  if (shared->mailbox->msg_count == 0)
2225  {
2226  mutt_error(_("failed to find message in notmuch database. try "
2227  "running 'notmuch new'."));
2228  break;
2229  }
2230  }
2231  priv->oldcount = shared->mailbox->msg_count;
2232  const int index = menu_get_index(priv->menu);
2233  struct Email *e_oldcur = mutt_get_virt_email(shared->mailbox, index);
2234  if (nm_read_entire_thread(shared->mailbox, e_oldcur) < 0)
2235  {
2236  mutt_message(_("Failed to read thread, aborting"));
2237  break;
2238  }
2239  if (priv->oldcount < shared->mailbox->msg_count)
2240  {
2241  /* nm_read_entire_thread() triggers mutt_sort_headers() if necessary */
2242  menu_set_index(priv->menu, e_oldcur->vnum);
2244 
2245  if (e_oldcur->collapsed || shared->ctx->collapsed)
2246  {
2247  menu_set_index(priv->menu, mutt_uncollapse_thread(e_oldcur));
2248  mutt_set_vnum(shared->mailbox);
2249  }
2250  }
2251  if (priv->in_pager)
2252  {
2253  op = OP_DISPLAY_MESSAGE;
2254  continue;
2255  }
2256  break;
2257  }
2258 #endif
2259 
2260  case OP_MAIN_MODIFY_TAGS:
2261  case OP_MAIN_MODIFY_TAGS_THEN_HIDE:
2262  {
2263  if (!shared->mailbox)
2264  break;
2265  struct Mailbox *m = shared->mailbox;
2266  if (!mx_tags_is_supported(m))
2267  {
2268  mutt_message(_("Folder doesn't support tagging, aborting"));
2269  break;
2270  }
2271  if (!prereq(shared->ctx, priv->menu,
2273  break;
2274  if (!shared->email)
2275  break;
2276  char *tags = NULL;
2277  if (!priv->tag)
2278  tags = driver_tags_get_with_hidden(&shared->email->tags);
2279  char buf[PATH_MAX] = { 0 };
2280  int rc = mx_tags_edit(m, tags, buf, sizeof(buf));
2281  FREE(&tags);
2282  if (rc < 0)
2283  break;
2284  else if (rc == 0)
2285  {
2286  mutt_message(_("No tag specified, aborting"));
2287  break;
2288  }
2289 
2290  if (priv->tag)
2291  {
2292  struct Progress progress;
2293 
2294  if (m->verbose)
2295  {
2296  mutt_progress_init(&progress, _("Update tags..."),
2298  }
2299 
2300 #ifdef USE_NOTMUCH
2301  if (m->type == MUTT_NOTMUCH)
2302  nm_db_longrun_init(m, true);
2303 #endif
2304  for (int px = 0, i = 0; i < m->msg_count; i++)
2305  {
2306  struct Email *e = m->emails[i];
2307  if (!e)
2308  break;
2309  if (!message_is_tagged(e))
2310  continue;
2311 
2312  if (m->verbose)
2313  mutt_progress_update(&progress, ++px, -1);
2314  mx_tags_commit(m, e, buf);
2315  if (op == OP_MAIN_MODIFY_TAGS_THEN_HIDE)
2316  {
2317  bool still_queried = false;
2318 #ifdef USE_NOTMUCH
2319  if (m->type == MUTT_NOTMUCH)
2320  still_queried = nm_message_is_still_queried(m, e);
2321 #endif
2322  e->quasi_deleted = !still_queried;
2323  m->changed = true;
2324  }
2325  }
2326 #ifdef USE_NOTMUCH
2327  if (m->type == MUTT_NOTMUCH)
2328  nm_db_longrun_done(m);
2329 #endif
2331  }
2332  else
2333  {
2334  if (mx_tags_commit(m, shared->email, buf))
2335  {
2336  mutt_message(_("Failed to modify tags, aborting"));
2337  break;
2338  }
2339  if (op == OP_MAIN_MODIFY_TAGS_THEN_HIDE)
2340  {
2341  bool still_queried = false;
2342 #ifdef USE_NOTMUCH
2343  if (m->type == MUTT_NOTMUCH)
2344  still_queried = nm_message_is_still_queried(m, shared->email);
2345 #endif
2346  shared->email->quasi_deleted = !still_queried;
2347  m->changed = true;
2348  }
2349  if (priv->in_pager)
2350  {
2351  op = OP_DISPLAY_MESSAGE;
2352  continue;
2353  }
2354  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
2355  if (c_resolve)
2356  {
2357  int index = menu_get_index(priv->menu);
2358  index = ci_next_undeleted(shared->mailbox, index);
2359  if (index == -1)
2360  {
2362  }
2363  else
2364  {
2365  menu_set_index(priv->menu, index);
2366  }
2367  }
2368  else
2370  }
2372  break;
2373  }
2374 
2375  case OP_CHECK_STATS:
2376  mutt_check_stats(shared->mailbox);
2377  break;
2378 
2379 #ifdef USE_NOTMUCH
2380  case OP_MAIN_VFOLDER_FROM_QUERY:
2381  case OP_MAIN_VFOLDER_FROM_QUERY_READONLY:
2382  {
2383  char buf[PATH_MAX] = { 0 };
2384  if ((mutt_get_field("Query: ", buf, sizeof(buf), MUTT_NM_QUERY, false, NULL, NULL) != 0) ||
2385  (buf[0] == '\0'))
2386  {
2387  mutt_message(_("No query, aborting"));
2388  break;
2389  }
2390 
2391  // Keep copy of user's query to name the mailbox
2392  char *query_unencoded = mutt_str_dup(buf);
2393 
2394  struct Mailbox *m_query =
2395  change_folder_notmuch(priv->menu, buf, sizeof(buf), &priv->oldcount, shared,
2396  (op == OP_MAIN_VFOLDER_FROM_QUERY_READONLY));
2397  if (m_query)
2398  {
2399  m_query->name = query_unencoded;
2400  query_unencoded = NULL;
2401  }
2402  else
2403  {
2404  FREE(&query_unencoded);
2405  }
2406 
2407  break;
2408  }
2409 
2410  case OP_MAIN_WINDOWED_VFOLDER_BACKWARD:
2411  {
2412  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
2413  break;
2414  const short c_nm_query_window_duration =
2415  cs_subset_number(shared->sub, "nm_query_window_duration");
2416  if (c_nm_query_window_duration <= 0)
2417  {
2418  mutt_message(_("Windowed queries disabled"));
2419  break;
2420  }
2421  const char *const c_nm_query_window_current_search =
2422  cs_subset_string(shared->sub, "nm_query_window_current_search");
2423  if (!c_nm_query_window_current_search)
2424  {
2425  mutt_message(_("No notmuch vfolder currently loaded"));
2426  break;
2427  }
2429  char buf[PATH_MAX] = { 0 };
2430  mutt_str_copy(buf, c_nm_query_window_current_search, sizeof(buf));
2431  change_folder_notmuch(priv->menu, buf, sizeof(buf), &priv->oldcount, shared, false);
2432  break;
2433  }
2434 
2435  case OP_MAIN_WINDOWED_VFOLDER_FORWARD:
2436  {
2437  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
2438  break;
2439  const short c_nm_query_window_duration =
2440  cs_subset_number(shared->sub, "nm_query_window_duration");
2441  if (c_nm_query_window_duration <= 0)
2442  {
2443  mutt_message(_("Windowed queries disabled"));
2444  break;
2445  }
2446  const char *const c_nm_query_window_current_search =
2447  cs_subset_string(shared->sub, "nm_query_window_current_search");
2448  if (!c_nm_query_window_current_search)
2449  {
2450  mutt_message(_("No notmuch vfolder currently loaded"));
2451  break;
2452  }
2454  char buf[PATH_MAX] = { 0 };
2455  mutt_str_copy(buf, c_nm_query_window_current_search, sizeof(buf));
2456  change_folder_notmuch(priv->menu, buf, sizeof(buf), &priv->oldcount, shared, false);
2457  break;
2458  }
2459 #endif
2460 
2461 #ifdef USE_SIDEBAR
2462  case OP_SIDEBAR_OPEN:
2463  {
2464  struct MuttWindow *win_sidebar = mutt_window_find(dlg, WT_SIDEBAR);
2465  change_folder_mailbox(priv->menu, sb_get_highlight(win_sidebar),
2466  &priv->oldcount, shared, false);
2467  break;
2468  }
2469 #endif
2470 
2471  case OP_MAIN_NEXT_UNREAD_MAILBOX:
2472  {
2473  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
2474  break;
2475 
2476  struct Mailbox *m = shared->mailbox;
2477 
2478  struct Buffer *folderbuf = mutt_buffer_pool_get();
2479  mutt_buffer_strcpy(folderbuf, mailbox_path(m));
2480  m = mutt_mailbox_next(m, folderbuf);
2481  mutt_buffer_pool_release(&folderbuf);
2482 
2483  if (!m)
2484  {
2485  mutt_error(_("No mailboxes have new mail"));
2486  break;
2487  }
2488 
2489  change_folder_mailbox(priv->menu, m, &priv->oldcount, shared, false);
2490  break;
2491  }
2492 
2493  case OP_MAIN_CHANGE_FOLDER:
2494  case OP_MAIN_CHANGE_FOLDER_READONLY:
2495 #ifdef USE_NOTMUCH
2496  case OP_MAIN_CHANGE_VFOLDER: // now an alias for OP_MAIN_CHANGE_FOLDER
2497 #endif
2498  {
2499  bool pager_return = true; /* return to display message in pager */
2500  struct Buffer *folderbuf = mutt_buffer_pool_get();
2501  mutt_buffer_alloc(folderbuf, PATH_MAX);
2502 
2503  char *cp = NULL;
2504  bool read_only;
2505  const bool c_read_only = cs_subset_bool(shared->sub, "read_only");
2506  if (priv->attach_msg || c_read_only || (op == OP_MAIN_CHANGE_FOLDER_READONLY))
2507  {
2508  cp = _("Open mailbox in read-only mode");
2509  read_only = true;
2510  }
2511  else
2512  {
2513  cp = _("Open mailbox");
2514  read_only = false;
2515  }
2516 
2517  const bool c_change_folder_next =
2518  cs_subset_bool(shared->sub, "change_folder_next");
2519  if (c_change_folder_next && shared->mailbox &&
2520  !mutt_buffer_is_empty(&shared->mailbox->pathbuf))
2521  {
2522  mutt_buffer_strcpy(folderbuf, mailbox_path(shared->mailbox));
2523  mutt_buffer_pretty_mailbox(folderbuf);
2524  }
2525  /* By default, fill buf with the next mailbox that contains unread mail */
2526  mutt_mailbox_next(shared->ctx ? shared->mailbox : NULL, folderbuf);
2527 
2528  if (mutt_buffer_enter_fname(cp, folderbuf, true, shared->mailbox, false,
2529  NULL, NULL, MUTT_SEL_NO_FLAGS) == -1)
2530  goto changefoldercleanup;
2531 
2532  /* Selected directory is okay, let's save it. */
2534 
2535  if (mutt_buffer_is_empty(folderbuf))
2536  {
2538  goto changefoldercleanup;
2539  }
2540 
2541  struct Mailbox *m = mx_mbox_find2(mutt_buffer_string(folderbuf));
2542  if (m)
2543  {
2544  change_folder_mailbox(priv->menu, m, &priv->oldcount, shared, read_only);
2545  pager_return = false;
2546  }
2547  else
2548  {
2549  change_folder_string(priv->menu, folderbuf->data, folderbuf->dsize,
2550  &priv->oldcount, shared, &pager_return, read_only);
2551  }
2552 
2553  changefoldercleanup:
2554  mutt_buffer_pool_release(&folderbuf);
2555  if (priv->in_pager && pager_return)
2556  {
2557  op = OP_DISPLAY_MESSAGE;
2558  continue;
2559  }
2560  break;
2561  }
2562 
2563 #ifdef USE_NNTP
2564  case OP_MAIN_CHANGE_GROUP:
2565  case OP_MAIN_CHANGE_GROUP_READONLY:
2566  {
2567  bool pager_return = true; /* return to display message in pager */
2568  struct Buffer *folderbuf = mutt_buffer_pool_get();
2569  mutt_buffer_alloc(folderbuf, PATH_MAX);
2570 
2571  OptNews = false;
2572  bool read_only;
2573  char *cp = NULL;
2574  const bool c_read_only = cs_subset_bool(shared->sub, "read_only");
2575  if (priv->attach_msg || c_read_only || (op == OP_MAIN_CHANGE_GROUP_READONLY))
2576  {
2577  cp = _("Open newsgroup in read-only mode");
2578  read_only = true;
2579  }
2580  else
2581  {
2582  cp = _("Open newsgroup");
2583  read_only = false;
2584  }
2585 
2586  const bool c_change_folder_next =
2587  cs_subset_bool(shared->sub, "change_folder_next");
2588  if (c_change_folder_next && shared->mailbox &&
2589  !mutt_buffer_is_empty(&shared->mailbox->pathbuf))
2590  {
2591  mutt_buffer_strcpy(folderbuf, mailbox_path(shared->mailbox));
2592  mutt_buffer_pretty_mailbox(folderbuf);
2593  }
2594 
2595  OptNews = true;
2596  const char *const c_news_server =
2597  cs_subset_string(shared->sub, "news_server");
2598  CurrentNewsSrv = nntp_select_server(shared->mailbox, c_news_server, false);
2599  if (!CurrentNewsSrv)
2600  goto changefoldercleanup2;
2601 
2602  nntp_mailbox(shared->mailbox, folderbuf->data, folderbuf->dsize);
2603 
2604  if (mutt_buffer_enter_fname(cp, folderbuf, true, shared->mailbox, false,
2605  NULL, NULL, MUTT_SEL_NO_FLAGS) == -1)
2606  goto changefoldercleanup2;
2607 
2608  /* Selected directory is okay, let's save it. */
2610 
2611  if (mutt_buffer_is_empty(folderbuf))
2612  {
2614  goto changefoldercleanup2;
2615  }
2616 
2617  struct Mailbox *m = mx_mbox_find2(mutt_buffer_string(folderbuf));
2618  if (m)
2619  {
2620  change_folder_mailbox(priv->menu, m, &priv->oldcount, shared, read_only);
2621  pager_return = false;
2622  }
2623  else
2624  {
2625  change_folder_string(priv->menu, folderbuf->data, folderbuf->dsize,
2626  &priv->oldcount, shared, &pager_return, read_only);
2627  }
2628  dlg->help_data = IndexNewsHelp;
2629 
2630  changefoldercleanup2:
2631  mutt_buffer_pool_release(&folderbuf);
2632  if (priv->in_pager && pager_return)
2633  {
2634  op = OP_DISPLAY_MESSAGE;
2635  continue;
2636  }
2637  break;
2638  }
2639 #endif
2640 
2641  case OP_DISPLAY_MESSAGE:
2642  case OP_DISPLAY_HEADERS: /* don't weed the headers */
2643  {
2644  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2645  break;
2646  if (!shared->email)
2647  break;
2648  /* toggle the weeding of headers so that a user can press the key
2649  * again while reading the message. */
2650  if (op == OP_DISPLAY_HEADERS)
2651  bool_str_toggle(shared->sub, "weed", NULL);
2652 
2653  OptNeedResort = false;
2654 
2655  const short c_sort = cs_subset_sort(shared->sub, "sort");
2656  if (((c_sort & SORT_MASK) == SORT_THREADS) && shared->email->collapsed)
2657  {
2658  mutt_uncollapse_thread(shared->email);
2659  mutt_set_vnum(shared->mailbox);
2660  const bool c_uncollapse_jump =
2661  cs_subset_bool(shared->sub, "uncollapse_jump");
2662  if (c_uncollapse_jump)
2664  }
2665 
2666  const bool c_pgp_auto_decode =
2667  cs_subset_bool(shared->sub, "pgp_auto_decode");
2668  if (c_pgp_auto_decode &&
2669  (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED)))
2670  {
2671  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2672  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
2673  if (mutt_check_traditional_pgp(shared->mailbox, &el))
2675  emaillist_clear(&el);
2676  }
2677  const int index = menu_get_index(priv->menu);
2678  index_shared_data_set_email(shared, mutt_get_virt_email(shared->mailbox, index));
2679 
2680  op = mutt_display_message(priv->win_index, priv->win_ibar, priv->win_pager,
2681  priv->win_pbar, shared->mailbox, shared->email);
2682  window_set_focus(priv->win_index);
2683  if (op < 0)
2684  {
2685  OptNeedResort = false;
2686  break;
2687  }
2688 
2689  /* This is used to redirect a single operation back here afterwards. If
2690  * mutt_display_message() returns 0, then this flag and pager state will
2691  * be cleaned up after this switch statement. */
2692  priv->in_pager = true;
2693  if (shared->mailbox)
2694  {
2695  update_index(priv->menu, shared->ctx, MX_STATUS_NEW_MAIL,
2696  shared->mailbox->msg_count, shared);
2697  }
2698  continue;
2699  }
2700 
2701  case OP_EXIT:
2702  if ((!priv->in_pager) && priv->attach_msg)
2703  {
2704  priv->done = true;
2705  break;
2706  }
2707 
2708  const enum QuadOption c_quit = cs_subset_quad(shared->sub, "quit");
2709  if ((!priv->in_pager) &&
2710  (query_quadoption(c_quit, _("Exit NeoMutt without saving?")) == MUTT_YES))
2711  {
2712  if (shared->ctx)
2713  {
2714  struct Context *ctx = shared->ctx;
2715  index_shared_data_set_context(shared, NULL);
2716  mx_fastclose_mailbox(shared->mailbox);
2717  ctx_free(&ctx);
2718  }
2719  priv->done = true;
2720  }
2721  break;
2722 
2723  case OP_MAIN_BREAK_THREAD:
2724  {
2725  if (!prereq(shared->ctx, priv->menu,
2727  break;
2728  /* L10N: CHECK_ACL */
2729  if (!check_acl(shared->mailbox, MUTT_ACL_WRITE, _("Can't break thread")))
2730  break;
2731  if (!shared->email)
2732  break;
2733 
2734  const short c_sort = cs_subset_sort(shared->sub, "sort");
2735  if ((c_sort & SORT_MASK) != SORT_THREADS)
2736  mutt_error(_("Threading is not enabled"));
2737  else if (!STAILQ_EMPTY(&shared->email->env->in_reply_to) ||
2738  !STAILQ_EMPTY(&shared->email->env->references))
2739  {
2740  {
2741  mutt_break_thread(shared->email);
2742  mutt_sort_headers(shared->mailbox, shared->ctx->threads, true,
2743  &shared->ctx->vsize);
2744  menu_set_index(priv->menu, shared->email->vnum);
2745  }
2746 
2747  shared->mailbox->changed = true;
2748  mutt_message(_("Thread broken"));
2749 
2750  if (priv->in_pager)
2751  {
2752  op = OP_DISPLAY_MESSAGE;
2753  continue;
2754  }
2755  else
2757  }
2758  else
2759  {
2760  mutt_error(
2761  _("Thread can't be broken, message is not part of a thread"));
2762  }
2763  break;
2764  }
2765 
2766  case OP_MAIN_LINK_THREADS:
2767  {
2768  if (!prereq(shared->ctx, priv->menu,
2770  break;
2771  /* L10N: CHECK_ACL */
2772  if (!check_acl(shared->mailbox, MUTT_ACL_WRITE, _("Can't link threads")))
2773  break;
2774  if (!shared->email)
2775  break;
2776 
2777  const short c_sort = cs_subset_sort(shared->sub, "sort");
2778  if ((c_sort & SORT_MASK) != SORT_THREADS)
2779  mutt_error(_("Threading is not enabled"));
2780  else if (!shared->email->env->message_id)
2781  mutt_error(_("No Message-ID: header available to link thread"));
2782  else
2783  {
2784  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2785  el_add_tagged(&el, shared->ctx, NULL, true);
2786 
2787  if (mutt_link_threads(shared->email, &el, shared->mailbox))
2788  {
2789  mutt_sort_headers(shared->mailbox, shared->ctx->threads, true,
2790  &shared->ctx->vsize);
2791  menu_set_index(priv->menu, shared->email->vnum);
2792 
2793  shared->mailbox->changed = true;
2794  mutt_message(_("Threads linked"));
2795  }
2796  else
2797  mutt_error(_("No thread linked"));
2798 
2799  emaillist_clear(&el);
2800  }
2801 
2802  if (priv->in_pager)
2803  {
2804  op = OP_DISPLAY_MESSAGE;
2805  continue;
2806  }
2807  else
2809 
2810  break;
2811  }
2812 
2813  case OP_EDIT_TYPE:
2814  {
2815  if (!prereq(shared->ctx, priv->menu,
2817  break;
2818  if (!shared->email)
2819  break;
2820  mutt_edit_content_type(shared->email, shared->email->body, NULL);
2821  /* if we were in the pager, redisplay the message */
2822  if (priv->in_pager)
2823  {
2824  op = OP_DISPLAY_MESSAGE;
2825  continue;
2826  }
2827  else
2829  break;
2830  }
2831 
2832  case OP_MAIN_NEXT_UNDELETED:
2833  {
2834  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2835  break;
2836  int index = menu_get_index(priv->menu);
2837  if (index >= (shared->mailbox->vcount - 1))
2838  {
2839  if (!priv->in_pager)
2840  mutt_message(_("You are on the last message"));
2841  break;
2842  }
2843  index = ci_next_undeleted(shared->mailbox, index);
2844  if (index != -1)
2845  menu_set_index(priv->menu, index);
2846 
2847  if (index == -1)
2848  {
2849  if (!priv->in_pager)
2850  mutt_error(_("No undeleted messages"));
2851  }
2852  else if (priv->in_pager)
2853  {
2854  op = OP_DISPLAY_MESSAGE;
2855  continue;
2856  }
2857  else
2859  break;
2860  }
2861 
2862  case OP_NEXT_ENTRY:
2863  {
2864  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2865  break;
2866  const int index = menu_get_index(priv->menu) + 1;
2867  if (index >= shared->mailbox->vcount)
2868  {
2869  if (!priv->in_pager)
2870  mutt_message(_("You are on the last message"));
2871  break;
2872  }
2873  menu_set_index(priv->menu, index);
2874  if (priv->in_pager)
2875  {
2876  op = OP_DISPLAY_MESSAGE;
2877  continue;
2878  }
2879  else
2881  break;
2882  }
2883 
2884  case OP_MAIN_PREV_UNDELETED:
2885  {
2886  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2887  break;
2888  int index = menu_get_index(priv->menu);
2889  if (index < 1)
2890  {
2891  mutt_message(_("You are on the first message"));
2892  break;
2893  }
2894  index = ci_previous_undeleted(shared->mailbox, index);
2895  if (index != -1)
2896  menu_set_index(priv->menu, index);
2897 
2898  if (index == -1)
2899  {
2900  if (!priv->in_pager)
2901  mutt_error(_("No undeleted messages"));
2902  }
2903  else if (priv->in_pager)
2904  {
2905  op = OP_DISPLAY_MESSAGE;
2906  continue;
2907  }
2908  else
2910  break;
2911  }
2912 
2913  case OP_PREV_ENTRY:
2914  {
2915  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2916  break;
2917  int index = menu_get_index(priv->menu);
2918  if (index < 1)
2919  {
2920  if (!priv->in_pager)
2921  mutt_message(_("You are on the first message"));
2922  break;
2923  }
2924  menu_set_index(priv->menu, index - 1);
2925  if (priv->in_pager)
2926  {
2927  op = OP_DISPLAY_MESSAGE;
2928  continue;
2929  }
2930  else
2932  break;
2933  }
2934 
2935  case OP_DECRYPT_COPY:
2936  case OP_DECRYPT_SAVE:
2937  if (!WithCrypto)
2938  break;
2939  /* fallthrough */
2940  case OP_COPY_MESSAGE:
2941  case OP_SAVE:
2942  case OP_DECODE_COPY:
2943  case OP_DECODE_SAVE:
2944  {
2945  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2946  break;
2947  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2948  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
2949 
2950  const enum MessageSaveOpt save_opt =
2951  ((op == OP_SAVE) || (op == OP_DECODE_SAVE) || (op == OP_DECRYPT_SAVE)) ?
2952  SAVE_MOVE :
2953  SAVE_COPY;
2954 
2955  enum MessageTransformOpt transform_opt =
2956  ((op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY)) ? TRANSFORM_DECODE :
2957  ((op == OP_DECRYPT_SAVE) || (op == OP_DECRYPT_COPY)) ? TRANSFORM_DECRYPT :
2959 
2960  const int rc = mutt_save_message(shared->mailbox, &el, save_opt, transform_opt);
2961  if ((rc == 0) && (save_opt == SAVE_MOVE))
2962  {
2964  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
2965  if (priv->tag)
2967  else if (c_resolve)
2968  {
2969  int index = menu_get_index(priv->menu);
2970  index = ci_next_undeleted(shared->mailbox, index);
2971  if (index == -1)
2972  {
2974  }
2975  else
2976  {
2977  menu_set_index(priv->menu, index);
2978  }
2979  }
2980  else
2982  }
2983  emaillist_clear(&el);
2984  break;
2985  }
2986 
2987  case OP_MAIN_NEXT_NEW:
2988  case OP_MAIN_NEXT_UNREAD:
2989  case OP_MAIN_PREV_NEW:
2990  case OP_MAIN_PREV_UNREAD:
2991  case OP_MAIN_NEXT_NEW_THEN_UNREAD:
2992  case OP_MAIN_PREV_NEW_THEN_UNREAD:
2993  {
2994  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2995  break;
2996 
2997  int first_unread = -1;
2998  int first_new = -1;
2999 
3000  const int saved_current = menu_get_index(priv->menu);
3001  int mcur = saved_current;
3002  menu_set_index(priv->menu, -1);
3003  for (size_t i = 0; i != shared->mailbox->vcount; i++)
3004  {
3005  if ((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_NEXT_UNREAD) ||
3006  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD))
3007  {
3008  mcur++;
3009  if (mcur > (shared->mailbox->vcount - 1))
3010  {
3011  mcur = 0;
3012  }
3013  }
3014  else
3015  {
3016  mcur--;
3017  if (mcur < 0)
3018  {
3019  mcur = shared->mailbox->vcount - 1;
3020  }
3021  }
3022 
3023  struct Email *e = mutt_get_virt_email(shared->mailbox, mcur);
3024  if (!e)
3025  break;
3026  const short c_sort = cs_subset_sort(shared->sub, "sort");
3027  if (e->collapsed && ((c_sort & SORT_MASK) == SORT_THREADS))
3028  {
3029  int unread = mutt_thread_contains_unread(e);
3030  if ((unread != 0) && (first_unread == -1))
3031  first_unread = mcur;
3032  if ((unread == 1) && (first_new == -1))
3033  first_new = mcur;
3034  }
3035  else if (!e->deleted && !e->read)
3036  {
3037  if (first_unread == -1)
3038  first_unread = mcur;
3039  if (!e->old && (first_new == -1))
3040  first_new = mcur;
3041  }
3042 
3043  if (((op == OP_MAIN_NEXT_UNREAD) || (op == OP_MAIN_PREV_UNREAD)) &&
3044  (first_unread != -1))
3045  break;
3046  if (((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_PREV_NEW) ||
3047  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD) || (op == OP_MAIN_PREV_NEW_THEN_UNREAD)) &&
3048  (first_new != -1))
3049  {
3050  break;
3051  }
3052  }
3053  if (((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_PREV_NEW) ||
3054  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD) || (op == OP_MAIN_PREV_NEW_THEN_UNREAD)) &&
3055  (first_new != -1))
3056  {
3057  menu_set_index(priv->menu, first_new);
3058  }
3059  else if (((op == OP_MAIN_NEXT_UNREAD) || (op == OP_MAIN_PREV_UNREAD) ||
3060  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD) || (op == OP_MAIN_PREV_NEW_THEN_UNREAD)) &&
3061  (first_unread != -1))
3062  {
3063  menu_set_index(priv->menu, first_unread);
3064  }
3065 
3066  if (menu_get_index(priv->menu) == -1)
3067  {
3068  menu_set_index(priv->menu, saved_current);
3069  if ((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_PREV_NEW))
3070  {
3071  if (ctx_has_limit(shared->ctx))
3072  mutt_error(_("No new messages in this limited view"));
3073  else
3074  mutt_error(_("No new messages"));
3075  }
3076  else
3077  {
3078  if (ctx_has_limit(shared->ctx))
3079  mutt_error(_("No unread messages in this limited view"));
3080  else
3081  mutt_error(_("No unread messages"));
3082  }
3083  break;
3084  }
3085 
3086  const int index = menu_get_index(priv->menu);
3087  if ((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_NEXT_UNREAD) ||
3088  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD))
3089  {
3090  if (saved_current > index)
3091  {
3092  mutt_message(_("Search wrapped to top"));
3093  }
3094  }
3095  else if (saved_current < index)
3096  {
3097  mutt_message(_("Search wrapped to bottom"));
3098  }
3099 
3100  if (priv->in_pager)
3101  {
3102  op = OP_DISPLAY_MESSAGE;
3103  continue;
3104  }
3105  else
3107  break;
3108  }
3109  case OP_FLAG_MESSAGE:
3110  {
3111  if (!prereq(shared->ctx, priv->menu,
3113  break;
3114  /* L10N: CHECK_ACL */
3115  if (!check_acl(shared->mailbox, MUTT_ACL_WRITE, _("Can't flag message")))
3116  break;
3117 
3118  struct Mailbox *m = shared->mailbox;
3119  if (priv->tag)
3120  {
3121  for (size_t i = 0; i < m->msg_count; i++)
3122  {
3123  struct Email *e = m->emails[i];
3124  if (!e)
3125  break;
3126  if (message_is_tagged(e))
3127  mutt_set_flag(m, e, MUTT_FLAG, !e->flagged);
3128  }
3129 
3131  }
3132  else
3133  {
3134  if (!shared->email)
3135  break;
3136  mutt_set_flag(m, shared->email, MUTT_FLAG, !shared->email->flagged);
3137  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
3138  if (c_resolve)
3139  {
3140  int index = menu_get_index(priv->menu);
3141  index = ci_next_undeleted(shared->mailbox, index);
3142  if (index == -1)
3143  {
3145  }
3146  else
3147  {
3148  menu_set_index(priv->menu, index);
3149  }
3150  }
3151  else
3153  }
3155  break;
3156  }
3157 
3158  case OP_TOGGLE_NEW:
3159  {
3160  if (!prereq(shared->ctx, priv->menu,
3162  break;
3163  /* L10N: CHECK_ACL */
3164  if (!check_acl(shared->mailbox, MUTT_ACL_SEEN, _("Can't toggle new")))
3165  break;
3166 
3167  struct Mailbox *m = shared->mailbox;
3168  if (priv->tag)
3169  {
3170  for (size_t i = 0; i < m->msg_count; i++)
3171  {
3172  struct Email *e = m->emails[i];
3173  if (!e)
3174  break;
3175  if (!message_is_tagged(e))
3176  continue;
3177 
3178  if (e->read || e->old)
3179  mutt_set_flag(m, e, MUTT_NEW, true);
3180  else
3181  mutt_set_flag(m, e, MUTT_READ, true);
3182  }
3184  }
3185  else
3186  {
3187  if (!shared->email)
3188  break;
3189  if (shared->email->read || shared->email->old)
3190  mutt_set_flag(m, shared->email, MUTT_NEW, true);
3191  else
3192  mutt_set_flag(m, shared->email, MUTT_READ, true);
3193 
3194  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
3195  if (c_resolve)
3196  {
3197  int index = menu_get_index(priv->menu);
3198  index = ci_next_undeleted(shared->mailbox, index);
3199  if (index == -1)
3200  {
3202  }
3203  else
3204  {
3205  menu_set_index(priv->menu, index);
3206  }
3207  }
3208  else
3211  }
3212  break;
3213  }
3214 
3215  case OP_TOGGLE_WRITE:
3216  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
3217  break;
3218  if (mx_toggle_write(shared->mailbox) == 0)
3219  {
3220  if (priv->in_pager)
3221  {
3222  op = OP_DISPLAY_MESSAGE;
3223  continue;
3224  }
3225  else
3227  }
3228  break;
3229 
3230  case OP_MAIN_NEXT_THREAD:
3231  case OP_MAIN_NEXT_SUBTHREAD:
3232  case OP_MAIN_PREV_THREAD:
3233  case OP_MAIN_PREV_SUBTHREAD:
3234  {
3235  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3236  break;
3237 
3238  int index = -1;
3239  switch (op)
3240  {
3241  case OP_MAIN_NEXT_THREAD:
3242  index = mutt_next_thread(shared->email);
3243  break;
3244 
3245  case OP_MAIN_NEXT_SUBTHREAD:
3246  index = mutt_next_subthread(shared->email);
3247  break;
3248 
3249  case OP_MAIN_PREV_THREAD:
3250  index = mutt_previous_thread(shared->email);
3251  break;
3252 
3253  case OP_MAIN_PREV_SUBTHREAD:
3254  index = mutt_previous_subthread(shared->email);
3255  break;
3256  }
3257 
3258  if (index != -1)
3259  menu_set_index(priv->menu, index);
3260 
3261  if (index < 0)
3262  {
3263  if ((op == OP_MAIN_NEXT_THREAD) || (op == OP_MAIN_NEXT_SUBTHREAD))
3264  mutt_error(_("No more threads"));
3265  else
3266  mutt_error(_("You are on the first thread"));
3267  }
3268  else if (priv->in_pager)
3269  {
3270  op = OP_DISPLAY_MESSAGE;
3271  continue;
3272  }
3273  else
3275  break;
3276  }
3277 
3278  case OP_MAIN_ROOT_MESSAGE:
3279  case OP_MAIN_PARENT_MESSAGE:
3280  {
3281  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3282  break;
3283 
3284  int index = mutt_parent_message(shared->email, op == OP_MAIN_ROOT_MESSAGE);
3285  if (index != -1)
3286  menu_set_index(priv->menu, index);
3287 
3288  if (priv->in_pager)
3289  {
3290  op = OP_DISPLAY_MESSAGE;
3291  continue;
3292  }
3293  else
3295  break;
3296  }
3297 
3298  case OP_MAIN_SET_FLAG:
3299  case OP_MAIN_CLEAR_FLAG:
3300  {
3301  if (!prereq(shared->ctx, priv->menu,
3303  break;
3304  /* check_acl(MUTT_ACL_WRITE); */
3305 
3306  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3307  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3308 
3309  if (mutt_change_flag(shared->mailbox, &el, (op == OP_MAIN_SET_FLAG)) == 0)
3310  {
3312  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
3313  if (priv->tag)
3315  else if (c_resolve)
3316  {
3317  int index = menu_get_index(priv->menu);
3318  index = ci_next_undeleted(shared->mailbox, index);
3319  if (index == -1)
3320  {
3322  }
3323  else
3324  {
3325  menu_set_index(priv->menu, index);
3326  }
3327  }
3328  else
3330  }
3331  emaillist_clear(&el);
3332  break;
3333  }
3334 
3335  case OP_MAIN_COLLAPSE_THREAD:
3336  {
3337  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3338  break;
3339 
3340  const short c_sort = cs_subset_sort(shared->sub, "sort");
3341  if ((c_sort & SORT_MASK) != SORT_THREADS)
3342  {
3343  mutt_error(_("Threading is not enabled"));
3344  break;
3345  }
3346 
3347  if (!shared->email)
3348  break;
3349 
3350  if (shared->email->collapsed)
3351  {
3353  mutt_set_vnum(shared->mailbox);
3354  const bool c_uncollapse_jump =
3355  cs_subset_bool(shared->sub, "uncollapse_jump");
3356  if (c_uncollapse_jump)
3358  }
3359  else if (mutt_thread_can_collapse(shared->email))
3360  {
3361  menu_set_index(priv->menu, mutt_collapse_thread(shared->email));
3362  mutt_set_vnum(shared->mailbox);
3363  }
3364  else
3365  {
3366  mutt_error(_("Thread contains unread or flagged messages"));
3367  break;
3368  }
3369 
3371 
3372  break;
3373  }
3374 
3375  case OP_MAIN_COLLAPSE_ALL:
3376  {
3377  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
3378  break;
3379 
3380  const short c_sort = cs_subset_sort(shared->sub, "sort");
3381  if ((c_sort & SORT_MASK) != SORT_THREADS)
3382  {
3383  mutt_error(_("Threading is not enabled"));
3384  break;
3385  }
3386  collapse_all(shared->ctx, priv->menu, 1);
3387  break;
3388  }
3389 
3390  /* --------------------------------------------------------------------
3391  * These functions are invoked directly from the internal-pager
3392  */
3393 
3394  case OP_BOUNCE_MESSAGE:
3395  {
3396  if (!prereq(shared->ctx, priv->menu,
3398  break;
3399  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3400  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3401  ci_bounce_message(shared->mailbox, &el);
3402  emaillist_clear(&el);
3403  break;
3404  }
3405 
3406  case OP_CREATE_ALIAS:
3407  {
3408  struct AddressList *al = NULL;
3409  if (shared->email && shared->email->env)
3410  al = mutt_get_address(shared->email->env, NULL);
3411  alias_create(al, shared->sub);
3413  break;
3414  }
3415 
3416  case OP_QUERY:
3417  if (!prereq(shared->ctx, priv->menu, CHECK_ATTACH))
3418  break;
3419  query_index(shared->sub);
3420  break;
3421 
3422  case OP_PURGE_MESSAGE:
3423  case OP_DELETE:
3424  {
3425  if (!prereq(shared->ctx, priv->menu,
3427  break;
3428  /* L10N: CHECK_ACL */
3429  if (!check_acl(shared->mailbox, MUTT_ACL_DELETE, _("Can't delete message")))
3430  break;
3431 
3432  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3433  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3434 
3435  mutt_emails_set_flag(shared->mailbox, &el, MUTT_DELETE, true);
3436  mutt_emails_set_flag(shared->mailbox, &el, MUTT_PURGE, (op == OP_PURGE_MESSAGE));
3437  const bool c_delete_untag = cs_subset_bool(shared->sub, "delete_untag");
3438  if (c_delete_untag)
3439  mutt_emails_set_flag(shared->mailbox, &el, MUTT_TAG, false);
3440  emaillist_clear(&el);
3441 
3442  if (priv->tag)
3443  {
3445  }
3446  else
3447  {
3448  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
3449  if (c_resolve)
3450  {
3451  int index = menu_get_index(priv->menu);
3452  index = ci_next_undeleted(shared->mailbox, index);
3453  if (index != -1)
3454  menu_set_index(priv->menu, index);
3455 
3456  if (index == -1)
3457  {
3459  }
3460  else if (priv->in_pager)
3461  {
3462  op = OP_DISPLAY_MESSAGE;
3463  continue;
3464  }
3465  }
3466  else
3468  }
3470  break;
3471  }
3472 
3473  case OP_DELETE_THREAD:
3474  case OP_DELETE_SUBTHREAD:
3475  case OP_PURGE_THREAD:
3476  {
3477  if (!prereq(shared->ctx, priv->menu,
3479  break;
3480  /* L10N: CHECK_ACL */
3481  /* L10N: Due to the implementation details we do not know whether we
3482  delete zero, 1, 12, ... messages. So in English we use
3483  "messages". Your language might have other means to express this. */
3484  if (!check_acl(shared->mailbox, MUTT_ACL_DELETE, _("Can't delete messages")))
3485  break;
3486  if (!shared->email)
3487  break;
3488 
3489  int subthread = (op == OP_DELETE_SUBTHREAD);
3490  int rc = mutt_thread_set_flag(shared->mailbox, shared->email,
3491  MUTT_DELETE, true, subthread);
3492  if (rc == -1)
3493  break;
3494  if (op == OP_PURGE_THREAD)
3495  {
3496  rc = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_PURGE,
3497  true, subthread);
3498  if (rc == -1)
3499  break;
3500  }
3501 
3502  const bool c_delete_untag = cs_subset_bool(shared->sub, "delete_untag");
3503  if (c_delete_untag)
3504  mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_TAG, false, subthread);
3505  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
3506  if (c_resolve)
3507  {
3508  int index = menu_get_index(priv->menu);
3509  index = ci_next_undeleted(shared->mailbox, index);
3510  if (index != -1)
3511  menu_set_index(priv->menu, index);
3512  }
3514  break;
3515  }
3516 
3517 #ifdef USE_NNTP
3518  case OP_CATCHUP:
3519  if (!prereq(shared->ctx, priv->menu,
3521  break;
3522  if (shared->mailbox && (shared->mailbox->type == MUTT_NNTP))
3523  {
3524  struct NntpMboxData *mdata = shared->mailbox->mdata;
3525  if (mutt_newsgroup_catchup(shared->mailbox, mdata->adata, mdata->group))
3527  }
3528  break;
3529 #endif
3530 
3531  case OP_DISPLAY_ADDRESS:
3532  {
3533  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3534  break;
3535  if (!shared->email)
3536  break;
3537  mutt_display_address(shared->email->env);
3538  break;
3539  }
3540 
3541  case OP_ENTER_COMMAND:
3543  window_set_focus(priv->win_index);
3544  mutt_check_rescore(shared->mailbox);
3546  break;
3547 
3548  case OP_EDIT_OR_VIEW_RAW_MESSAGE:
3549  case OP_EDIT_RAW_MESSAGE:
3550  case OP_VIEW_RAW_MESSAGE:
3551  {
3552  /* TODO split this into 3 cases? */
3553  if (!prereq(shared->ctx, priv->menu,
3555  break;
3556  bool edit;
3557  if (op == OP_EDIT_RAW_MESSAGE)
3558  {
3559  if (!prereq(shared->ctx, priv->menu, CHECK_READONLY))
3560  break;
3561  /* L10N: CHECK_ACL */
3562  if (!check_acl(shared->mailbox, MUTT_ACL_INSERT, _("Can't edit message")))
3563  break;
3564  edit = true;
3565  }
3566  else if (op == OP_EDIT_OR_VIEW_RAW_MESSAGE)
3567  edit = !shared->mailbox->readonly && (shared->mailbox->rights & MUTT_ACL_INSERT);
3568  else
3569  edit = false;
3570 
3571  if (!shared->email)
3572  break;
3573  const bool c_pgp_auto_decode =
3574  cs_subset_bool(shared->sub, "pgp_auto_decode");
3575  if (c_pgp_auto_decode &&
3576  (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED)))
3577  {
3578  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3579  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3580  if (mutt_check_traditional_pgp(shared->mailbox, &el))
3582  emaillist_clear(&el);
3583  }
3584  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3585  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3586  mutt_ev_message(shared->mailbox, &el, edit ? EVM_EDIT : EVM_VIEW);
3587  emaillist_clear(&el);
3589 
3590  break;
3591  }
3592 
3593  case OP_FORWARD_MESSAGE:
3594  {
3595  if (!prereq(shared->ctx, priv->menu,
3597  break;
3598  if (!shared->email)
3599  break;
3600  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3601  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3602  const bool c_pgp_auto_decode =
3603  cs_subset_bool(shared->sub, "pgp_auto_decode");
3604  if (c_pgp_auto_decode &&
3605  (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED)))
3606  {
3607  if (mutt_check_traditional_pgp(shared->mailbox, &el))
3609  }
3610  mutt_send_message(SEND_FORWARD, NULL, NULL, shared->mailbox, &el, shared->sub);
3611  emaillist_clear(&el);
3613  break;
3614  }
3615 
3616  case OP_FORGET_PASSPHRASE:
3618  break;
3619 
3620  case OP_GROUP_REPLY:
3621  case OP_GROUP_CHAT_REPLY:
3622  {
3623  SendFlags replyflags = SEND_REPLY;
3624  if (op == OP_GROUP_REPLY)
3625  replyflags |= SEND_GROUP_REPLY;
3626  else
3627  replyflags |= SEND_GROUP_CHAT_REPLY;
3628  if (!prereq(shared->ctx, priv->menu,
3630  break;
3631  if (!shared->email)
3632  break;
3633  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3634  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3635  const bool c_pgp_auto_decode =
3636  cs_subset_bool(shared->sub, "pgp_auto_decode");
3637  if (c_pgp_auto_decode &&
3638  (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED)))
3639  {
3640  if (mutt_check_traditional_pgp(shared->mailbox, &el))
3642  }
3643  mutt_send_message(replyflags, NULL, NULL, shared->mailbox, &el, shared->sub);
3644  emaillist_clear(&el);
3646  break;
3647  }
3648 
3649  case OP_EDIT_LABEL:
3650  {
3651  if (!prereq(shared->ctx, priv->menu,
3653  break;
3654 
3655  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3656  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3657  int num_changed = mutt_label_message(shared->mailbox, &el);
3658  emaillist_clear(&el);
3659 
3660  if (num_changed > 0)
3661  {
3662  shared->mailbox->changed = true;
3664  /* L10N: This is displayed when the x-label on one or more
3665  messages is edited. */
3666  mutt_message(ngettext("%d label changed", "%d labels changed", num_changed),
3667  num_changed);
3668  }
3669  else
3670  {
3671  /* L10N: This is displayed when editing an x-label, but no messages
3672  were updated. Possibly due to canceling at the prompt or if the new
3673  label is the same as the old label. */
3674  mutt_message(_("No labels changed"));
3675  }
3676  break;
3677  }
3678 
3679  case OP_LIST_REPLY:
3680  {
3681  if (!prereq(shared->ctx, priv->menu,
3683  break;
3684  if (!shared->email)
3685  break;
3686  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3687  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3688  const bool c_pgp_auto_decode =
3689  cs_subset_bool(shared->sub, "pgp_auto_decode");
3690  if (c_pgp_auto_decode &&
3691  (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED)))
3692  {
3693  if (mutt_check_traditional_pgp(shared->mailbox, &el))
3695  }
3697  shared->mailbox, &el, shared->sub);
3698  emaillist_clear(&el);
3700  break;
3701  }
3702 
3703  case OP_MAIL:
3704  if (!prereq(shared->ctx, priv->menu, CHECK_ATTACH))
3705  break;
3706  mutt_send_message(SEND_NO_FLAGS, NULL, NULL, shared->mailbox, NULL,
3707  shared->sub);
3709  break;
3710 
3711  case OP_MAIL_KEY:
3712  if (!(WithCrypto & APPLICATION_PGP))
3713  break;
3714  if (!prereq(shared->ctx, priv->menu, CHECK_ATTACH))
3715  break;
3716  mutt_send_message(SEND_KEY, NULL, NULL, NULL, NULL, shared->sub);
3718  break;
3719 
3720  case OP_EXTRACT_KEYS:
3721  {
3722  if (!WithCrypto)
3723  break;
3724  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3725  break;
3726  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3727  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3729  emaillist_clear(&el);
3731  break;
3732  }
3733 
3734  case OP_CHECK_TRADITIONAL:
3735  {
3736  if (!(WithCrypto & APPLICATION_PGP))
3737  break;
3738  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3739  break;
3740  if (!shared->email)
3741  break;
3742  if (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED))
3743  {
3744  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3745  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3746  if (mutt_check_traditional_pgp(shared->mailbox, &el))
3748  emaillist_clear(&el);
3749  }
3750 
3751  if (priv->in_pager)
3752  {
3753  op = OP_DISPLAY_MESSAGE;
3754  continue;
3755  }
3756  break;
3757  }
3758 
3759  case OP_PIPE:
3760  {
3761  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3762  break;
3763  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3764  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3765  mutt_pipe_message(shared->mailbox, &el);
3766  emaillist_clear(&el);
3767 
3768 #ifdef USE_IMAP
3769  /* in an IMAP folder index with imap_peek=no, piping could change
3770  * new or old messages status to read. Redraw what's needed. */
3771  const bool c_imap_peek = cs_subset_bool(shared->sub, "imap_peek");
3772  if ((shared->mailbox->type == MUTT_IMAP) && !c_imap_peek)
3773  {
3775  }
3776 #endif
3777  break;
3778  }
3779 
3780  case OP_PRINT:
3781  {
3782  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3783  break;
3784  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3785  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3786  mutt_print_message(shared->mailbox, &el);
3787  emaillist_clear(&el);
3788 
3789 #ifdef USE_IMAP
3790  /* in an IMAP folder index with imap_peek=no, printing could change
3791  * new or old messages status to read. Redraw what's needed. */
3792  const bool c_imap_peek = cs_subset_bool(shared->sub, "imap_peek");
3793  if ((shared->mailbox->type == MUTT_IMAP) && !c_imap_peek)
3794  {
3796  }
3797 #endif
3798  break;
3799  }
3800 
3801  case OP_MAIN_READ_THREAD:
3802  case OP_MAIN_READ_SUBTHREAD:
3803  {
3804  if (!prereq(shared->ctx, priv->menu,
3806  break;
3807  /* L10N: CHECK_ACL */
3808  /* L10N: Due to the implementation details we do not know whether we
3809  mark zero, 1, 12, ... messages as read. So in English we use
3810  "messages". Your language might have other means to express this. */
3811  if (!check_acl(shared->mailbox, MUTT_ACL_SEEN, _("Can't mark messages as read")))
3812  break;
3813 
3814  int rc = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_READ,
3815  true, (op != OP_MAIN_READ_THREAD));
3816  if (rc != -1)
3817  {
3818  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
3819  if (c_resolve)
3820  {
3821  int index =
3822  ((op == OP_MAIN_READ_THREAD) ? mutt_next_thread(shared->email) :
3823  mutt_next_subthread(shared->email));
3824  if (index != -1)
3825  menu_set_index(priv->menu, index);
3826 
3827  if (priv->in_pager)
3828  {
3829  op = OP_DISPLAY_MESSAGE;
3830  continue;
3831  }
3832  }
3834  }
3835  break;
3836  }
3837 
3838  case OP_MARK_MSG:
3839  {
3840  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3841  break;
3842  if (!shared->email)
3843  break;
3844  if (shared->email->env->message_id)
3845  {
3846  char buf2[128] = { 0 };
3847 
3848  /* L10N: This is the prompt for <mark-message>. Whatever they
3849  enter will be prefixed by $mark_macro_prefix and will become
3850  a macro hotkey to jump to the currently selected message. */
3851  if (!mutt_get_field(_("Enter macro stroke: "), buf2, sizeof(buf2),
3852  MUTT_COMP_NO_FLAGS, false, NULL, NULL) &&
3853  buf2[0])
3854  {
3855  const char *const c_mark_macro_prefix =
3856  cs_subset_string(shared->sub, "mark_macro_prefix");
3857  char str[256];
3858  snprintf(str, sizeof(str), "%s%s", c_mark_macro_prefix, buf2);
3859 
3860  struct Buffer *msg_id = mutt_buffer_pool_get();
3861  mutt_file_sanitize_regex(msg_id, shared->email->env->message_id);
3862  char macro[256];
3863  snprintf(macro, sizeof(macro), "<search>~i '%s'\n", mutt_buffer_string(msg_id));
3864  mutt_buffer_pool_release(&msg_id);
3865 
3866  /* L10N: "message hotkey" is the key bindings priv->menu description of a
3867  macro created by <mark-message>. */
3868  km_bind(str, MENU_MAIN, OP_MACRO, macro, _("message hotkey"));
3869 
3870  /* L10N: This is echoed after <mark-message> creates a new hotkey
3871  macro. %s is the hotkey string ($mark_macro_prefix followed
3872  by whatever they typed at the prompt.) */
3873  snprintf(buf2, sizeof(buf2), _("Message bound to %s"), str);
3874  mutt_message(buf2);
3875  mutt_debug(LL_DEBUG1, "Mark: %s => %s\n", str, macro);
3876  }
3877  }
3878  else
3879  {
3880  /* L10N: This error is printed if <mark-message> can't find a
3881  Message-ID for the currently selected message in the index. */
3882  mutt_error(_("No message ID to macro"));
3883  }
3884  break;
3885  }
3886 
3887  case OP_RECALL_MESSAGE:
3888  if (!prereq(shared->ctx, priv->menu, CHECK_ATTACH))
3889  break;
3890  mutt_send_message(SEND_POSTPONED, NULL, NULL, shared->mailbox, NULL,
3891  shared->sub);
3893  break;
3894 
3895  case OP_RESEND:
3896  if (!prereq(shared->ctx, priv->menu,
3898  break;
3899 
3900  if (priv->tag)
3901  {
3902  struct Mailbox *m = shared->mailbox;
3903  for (size_t i = 0; i < m->msg_count; i++)
3904  {
3905  struct Email *e = m->emails[i];
3906  if (!e)
3907  break;
3908  if (message_is_tagged(e))
3909  mutt_resend_message(NULL, shared->mailbox, e, shared->sub);
3910  }
3911  }
3912  else
3913  {
3914  mutt_resend_message(NULL, shared->mailbox, shared->email, shared->sub);
3915  }
3916 
3918  break;
3919 
3920 #ifdef USE_NNTP
3921  case OP_FOLLOWUP:
3922  case OP_FORWARD_TO_GROUP:
3923  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3924  break;
3925  /* fallthrough */
3926 
3927  case OP_POST:
3928  {
3929  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_ATTACH))
3930  break;
3931  if (!shared->email)
3932  break;
3933  const enum QuadOption c_followup_to_poster =
3934  cs_subset_quad(shared->sub, "followup_to_poster");
3935  if ((op != OP_FOLLOWUP) || !shared->email->env->followup_to ||
3936  !mutt_istr_equal(shared->email->env->followup_to, "poster") ||
3937  (query_quadoption(c_followup_to_poster,
3938  _("Reply by mail as poster prefers?")) != MUTT_YES))
3939  {
3940  const enum QuadOption c_post_moderated =
3941  cs_subset_quad(shared->sub, "post_moderated");
3942  if (shared->mailbox && (shared->mailbox->type == MUTT_NNTP) &&
3943  !((struct NntpMboxData *) shared->mailbox->mdata)->allowed && (query_quadoption(c_post_moderated, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES))
3944  {
3945  break;
3946  }
3947  if (op == OP_POST)
3948  mutt_send_message(SEND_NEWS, NULL, NULL, shared->mailbox, NULL,
3949  shared->sub);
3950  else
3951  {
3952  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT))
3953  break;
3954  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3955  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3956  mutt_send_message(((op == OP_FOLLOWUP) ? SEND_REPLY : SEND_FORWARD) | SEND_NEWS,
3957  NULL, NULL, shared->mailbox, &el, shared->sub);
3958  emaillist_clear(&el);
3959  }
3961  break;
3962  }
3963  }
3964 #endif
3965  /* fallthrough */
3966  case OP_REPLY:
3967  {
3968  if (!prereq(shared->ctx, priv->menu,
3970  break;
3971  if (!shared->email)
3972  break;
3973  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3974  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3975  const bool c_pgp_auto_decode =
3976  cs_subset_bool(shared->sub, "pgp_auto_decode");
3977  if (c_pgp_auto_decode &&
3978  (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED)))
3979  {
3980  if (mutt_check_traditional_pgp(shared->mailbox, &el))
3982  }
3983  mutt_send_message(SEND_REPLY, NULL, NULL, shared->mailbox, &el, shared->sub);
3984  emaillist_clear(&el);
3986  break;
3987  }
3988 
3989  case OP_SHELL_ESCAPE:
3990  if (mutt_shell_escape())
3991  {
3993  }
3994  break;
3995 
3996  case OP_TAG_THREAD:
3997  case OP_TAG_SUBTHREAD:
3998  {
3999  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
4000  break;
4001  if (!shared->email)
4002  break;
4003 
4004  int rc = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_TAG,
4005  !shared->email->tagged, (op != OP_TAG_THREAD));
4006  if (rc != -1)
4007  {
4008  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
4009  if (c_resolve)
4010  {
4011  int index;
4012  if (op == OP_TAG_THREAD)
4013  index = mutt_next_thread(shared->email);
4014  else
4015  index = mutt_next_subthread(shared->email);
4016 
4017  if (index != -1)
4018  menu_set_index(priv->menu, index);
4019  }
4021  }
4022  break;
4023  }
4024 
4025  case OP_UNDELETE:
4026  {
4027  if (!prereq(shared->ctx, priv->menu,
4029  break;
4030  /* L10N: CHECK_ACL */
4031  if (!check_acl(shared->mailbox, MUTT_ACL_DELETE, _("Can't undelete message")))
4032  break;
4033 
4034  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
4035  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
4036 
4037  mutt_emails_set_flag(shared->mailbox, &el, MUTT_DELETE, false);
4038  mutt_emails_set_flag(shared->mailbox, &el, MUTT_PURGE, false);
4039  emaillist_clear(&el);
4040 
4041  if (priv->tag)
4042  {
4044  }
4045  else
4046  {
4047  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
4048  const int index = menu_get_index(priv->menu) + 1;
4049  if (c_resolve && (index < shared->mailbox->vcount))
4050  {
4051  menu_set_index(priv->menu, index);
4052  }
4053  else
4055  }
4056 
4058  break;
4059  }
4060 
4061  case OP_UNDELETE_THREAD:
4062  case OP_UNDELETE_SUBTHREAD:
4063  {
4064  if (!prereq(shared->ctx, priv->menu,
4066  break;
4067  /* L10N: CHECK_ACL */
4068  /* L10N: Due to the implementation details we do not know whether we
4069  undelete zero, 1, 12, ... messages. So in English we use
4070  "messages". Your language might have other means to express this. */
4071  if (!check_acl(shared->mailbox, MUTT_ACL_DELETE, _("Can't undelete messages")))
4072  break;
4073 
4074  int rc = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_DELETE,
4075  false, (op != OP_UNDELETE_THREAD));
4076  if (rc != -1)
4077  {
4078  rc = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_PURGE,
4079  false, (op != OP_UNDELETE_THREAD));
4080  }
4081  if (rc != -1)
4082  {
4083  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
4084  if (c_resolve)
4085  {
4086  int index;
4087  if (op == OP_UNDELETE_THREAD)
4088  index = mutt_next_thread(shared->email);
4089  else
4090  index = mutt_next_subthread(shared->email);
4091 
4092  if (index != -1)
4093  menu_set_index(priv->menu, index);
4094  }
4096  }
4097  break;
4098  }
4099 
4100  case OP_VERSION:
4102  break;
4103 
4104  case OP_MAILBOX_LIST:
4106  break;
4107 
4108  case OP_VIEW_ATTACHMENTS:
4109  {
4110  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
4111  break;
4112  if (!shared->email)
4113  break;
4114  struct Message *msg = mx_msg_open(shared->mailbox, shared->email->msgno);
4115  if (msg)
4116  {
4117  dlg_select_attachment(NeoMutt->sub, shared->mailbox, shared->email, msg->fp);
4118  if (shared->email->attach_del)
4119  {
4120  shared->mailbox->changed = true;
4121  }
4122  mx_msg_close(shared->mailbox, &msg);
4123  }
4125  break;
4126  }
4127 
4128  case OP_END_COND:
4129  break;
4130 
4131  case OP_WHAT_KEY:
4132  mutt_what_key();
4133  break;
4134 
4135 #ifdef USE_SIDEBAR
4136  case OP_SIDEBAR_FIRST:
4137  case OP_SIDEBAR_LAST:
4138  case OP_SIDEBAR_NEXT:
4139  case OP_SIDEBAR_NEXT_NEW:
4140  case OP_SIDEBAR_PAGE_DOWN:
4141  case OP_SIDEBAR_PAGE_UP:
4142  case OP_SIDEBAR_PREV:
4143  case OP_SIDEBAR_PREV_NEW:
4144  {
4145  struct MuttWindow *win_sidebar = mutt_window_find(dlg, WT_SIDEBAR);
4146  if (!win_sidebar)
4147  break;
4148  sb_change_mailbox(win_sidebar, op);
4149  break;
4150  }
4151 
4152  case OP_SIDEBAR_TOGGLE_VISIBLE:
4153  bool_str_toggle(shared->sub, "sidebar_visible", NULL);
4154  mutt_window_reflow(NULL);
4155  break;
4156 #endif
4157 
4158 #ifdef USE_AUTOCRYPT
4159  case OP_AUTOCRYPT_ACCT_MENU:
4161  break;
4162 #endif
4163 
4164  default:
4165  if (!priv->in_pager)
4167  }
4168 
4169 #ifdef USE_NOTMUCH
4170  nm_db_debug_check(shared->mailbox);
4171 #endif
4172 
4173  if (priv->in_pager)
4174  {
4176  priv->in_pager = false;
4178  }
4179 
4180  if (priv->done)
4181  break;
4182  }
4183 
4184  struct Context *ctx = shared->ctx;
4185  struct Mailbox *m = ctx_mailbox(ctx);
4186  index_shared_data_set_context(shared, ctx_old);
4187  ctx_free(&ctx);
4188 
4189  return m;
4190 }
int km_dokey(enum MenuType menu)
Determine what a keypress should do.
Definition: keymap.c:661
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
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
The "current" mailbox.
Definition: context.h:37
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:206
void nm_query_window_forward(void)
Function to move the current search window forward in time.
Definition: notmuch.c:1709
static int ci_first_message(struct Mailbox *m)
Get index of first new message.
Definition: index.c:339
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
Decrypt message.
Definition: commands.h:42
bool mutt_mailbox_list(void)
List the mailboxes with new mail.
Definition: mutt_mailbox.c:222
#define NONULL(x)
Definition: string2.h:37
int msg_count
Total number of messages.
Definition: mailbox.h:91
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:66
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:71
#define WithCrypto
Definition: lib.h:113
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:252
#define SEND_TO_SENDER
Compose new email to sender.
Definition: send.h:51
void mutt_pipe_message(struct Mailbox *m, struct EmailList *el)
Pipe a message.
Definition: commands.c:757
The envelope/body of an email.
Definition: email.h:37
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:408
#define mutt_perror(...)
Definition: logging.h:85
#define MUTT_SHUTDOWN_HOOK
shutdown-hook: run when leaving NeoMutt
Definition: hook.h:59
struct Body * body
List of MIME parts.
Definition: email.h:91
void nm_query_window_backward(void)
Function to move the current search window backward in time.
Definition: notmuch.c:1730
struct Mailbox * ctx_mailbox(struct Context *ctx)
wrapper to get the mailbox in a Context, or NULL
Definition: context.c:440
void window_set_focus(struct MuttWindow *win)
Set the Window focus.
Definition: mutt_window.c:760
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
struct Email * email
Currently selected Email.
Definition: shared_data.h:42
struct Menu * menu
Menu controlling the index.
Definition: private_data.h:42
#define mutt_uncollapse_thread(e)
Definition: mutt_thread.h:76
#define mutt_collapse_thread(e)
Definition: mutt_thread.h:75
bool tag
tag-prefix has been pressed
Definition: private_data.h:36
Pager Bar containing status info about the Pager.
Definition: mutt_window.h:100
#define mutt_message(...)
Definition: logging.h:83
void mutt_emails_set_flag(struct Mailbox *m, struct EmailList *el, enum MessageType flag, bool bf)
Set flag on messages.
Definition: flags.c:351
int help_menu
Menu for key bindings, e.g. MENU_PAGER.
Definition: mutt_window.h:136
void mutt_check_rescore(struct Mailbox *m)
Do the emails need to have their scores recalculated?
Definition: score.c:64
Private state data for the Index.
Definition: private_data.h:33
void mutt_sort_headers(struct Mailbox *m, struct ThreadsContext *threads, bool init, off_t *vsize)
Sort emails by their headers.
Definition: sort.c:378
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:306
void mutt_display_address(struct Envelope *env)
Display the address of a message.
Definition: commands.c:992
struct ConfigSubset * sub
Config set to use.
Definition: shared_data.h:38
struct NntpAccountData * adata
Definition: mdata.h:47
bool mutt_shell_escape(void)
invoke a command in a subshell
Definition: commands.c:908
static void index_custom_redraw(struct Menu *menu)
Redraw the index - Implements Menu::custom_redraw()
Definition: index.c:1090
Progress tracks elements, according to $write_inc
Definition: progress.h:37
Messages in limited view.
Definition: mutt.h:101
struct MuttWindow * win_pbar
Window for the Pager Bar.
Definition: private_data.h:46
struct HashTable * mutt_make_id_hash(struct Mailbox *m)
Create a Hash Table for message-ids.
Definition: mutt_thread.c:1538
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:49
#define SEND_FORWARD
Forward email.
Definition: send.h:43
struct MuttWindow * mutt_window_find(struct MuttWindow *root, enum WindowType type)
Find a Window of a given type.
Definition: mutt_window.c:675
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:242
void mutt_resize_screen(void)
Update NeoMutt&#39;s opinion about the window size (CURSES)
Definition: resize.c:101
bool mutt_check_traditional_pgp(struct Mailbox *m, struct EmailList *el)
Check if a message has inline PGP content.
Definition: commands.c:1537
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
#define CHECK_ATTACH
Is the user in message-attach mode?
Definition: index.c:149
WHERE bool OptNeedResort
(pseudo) used to force a re-sort
Definition: options.h:43