curs_main.c 62.9 KB
Newer Older
Thomas Roessler's avatar
Thomas Roessler committed
1
/*
2
 * Copyright (C) 1996-2000,2002,2010,2012-2013 Michael R. Elkins <me@mutt.org>
3
 *
Thomas Roessler's avatar
Thomas Roessler committed
4 5 6 7
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
8
 *
Thomas Roessler's avatar
Thomas Roessler committed
9 10 11 12
 *     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.
13
 *
Thomas Roessler's avatar
Thomas Roessler committed
14 15
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
16
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17
 */
Thomas Roessler's avatar
Thomas Roessler committed
18

19 20 21 22
#if HAVE_CONFIG_H
# include "config.h"
#endif

Thomas Roessler's avatar
Thomas Roessler committed
23 24 25 26
#include "mutt.h"
#include "mutt_curses.h"
#include "mutt_menu.h"
#include "mailbox.h"
Thomas Roessler's avatar
Thomas Roessler committed
27
#include "mapping.h"
Thomas Roessler's avatar
Thomas Roessler committed
28
#include "sort.h"
29
#include "buffy.h"
30
#include "mx.h"
Thomas Roessler's avatar
Thomas Roessler committed
31

32 33 34 35
#ifdef USE_SIDEBAR
#include "sidebar.h"
#endif

36 37 38 39
#ifdef USE_POP
#include "pop.h"
#endif

Thomas Roessler's avatar
Thomas Roessler committed
40
#ifdef USE_IMAP
41
#include "imap_private.h"
Thomas Roessler's avatar
Thomas Roessler committed
42 43
#endif

44 45 46 47
#ifdef USE_INOTIFY
#include "monitor.h"
#endif

48
#include "mutt_crypt.h"
Thomas Roessler's avatar
Thomas Roessler committed
49 50 51 52 53 54 55 56 57 58


#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>

Thomas Roessler's avatar
Thomas Roessler committed
59 60
#include <assert.h>

Thomas Roessler's avatar
Thomas Roessler committed
61 62 63 64
static const char *No_mailbox_is_open = N_("No mailbox is open.");
static const char *There_are_no_messages = N_("There are no messages.");
static const char *Mailbox_is_read_only = N_("Mailbox is read-only.");
static const char *Function_not_permitted_in_attach_message_mode = N_("Function not permitted in attach-message mode.");
65
static const char *No_visible = N_("No visible messages.");
66

67 68 69 70 71 72 73
#define CHECK_IN_MAILBOX                        \
  if (!Context)                                 \
  {                                             \
    mutt_flushinp ();                           \
    mutt_error _(No_mailbox_is_open);           \
    break;                                      \
  }
74

75 76 77 78 79 80 81 82 83 84 85 86 87
#define CHECK_MSGCOUNT                          \
  if (!Context)                                 \
  {                                             \
    mutt_flushinp ();                           \
    mutt_error _(No_mailbox_is_open);           \
    break;                                      \
  }                                             \
  else if (!Context->msgcount)                  \
  {                                             \
    mutt_flushinp ();                           \
    mutt_error _(There_are_no_messages);        \
    break;                                      \
  }
Thomas Roessler's avatar
Thomas Roessler committed
88

89 90 91 92 93 94 95
#define CHECK_VISIBLE                                   \
  if (Context && menu->current >= Context->vcount)      \
  {                                                     \
    mutt_flushinp ();                                   \
    mutt_error _(No_visible);                           \
    break;                                              \
  }
96

97

98 99 100 101 102 103 104
#define CHECK_READONLY                          \
  if (Context->readonly)                        \
  {                                             \
    mutt_flushinp ();                           \
    mutt_error _(Mailbox_is_read_only);         \
    break;                                      \
  }
Thomas Roessler's avatar
Thomas Roessler committed
105

106 107 108 109 110 111 112 113
#define CHECK_ACL(aclbit,action)                                        \
  if (!mutt_bit_isset(Context->rights,aclbit))                          \
  {                                                                     \
    mutt_flushinp();                                                    \
    /* L10N: %s is one of the CHECK_ACL entries below. */               \
    mutt_error (_("%s: Operation not permitted by ACL"), action);       \
    break;                                                              \
  }
114

115 116 117 118 119 120 121
#define CHECK_ATTACH                                                    \
  if (option(OPTATTACHMSG))                                             \
  {                                                                     \
    mutt_flushinp ();                                                   \
    mutt_error _(Function_not_permitted_in_attach_message_mode);        \
    break;                                                              \
  }
122

Thomas Roessler's avatar
Thomas Roessler committed
123 124
#define CURHDR Context->hdrs[Context->v2r[menu->current]]
#define OLDHDR Context->hdrs[Context->v2r[menu->oldcurrent]]
125
#define UNREAD(h) mutt_thread_contains_unread (Context, h)
Thomas Roessler's avatar
Thomas Roessler committed
126

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
/* de facto standard escapes for tsl/fsl */
static char *tsl = "\033]0;";
static char *fsl = "\007";

/* terminal status capability check. terminfo must have been initialized. */
short mutt_ts_capability(void)
{
  char *term = getenv("TERM");
  char *tcaps;
  int tcapi;
  char **termp;
  char *known[] = {
    "color-xterm",
    "cygwin",
    "eterm",
    "kterm",
    "nxterm",
    "putty",
    "rxvt",
    "screen",
    "xterm",
    NULL
  };

  /* If tsl is set, then terminfo says that status lines work. */
  tcaps = tigetstr("tsl");
  if (tcaps && tcaps != (char *)-1 && *tcaps)
  {
    /* update the static defns of tsl/fsl from terminfo */
    tsl = safe_strdup(tcaps);

    tcaps = tigetstr("fsl");
    if (tcaps && tcaps != (char *)-1 && *tcaps)
      fsl = safe_strdup(tcaps);

    return 1;
  }

  /* If XT (boolean) is set, then this terminal supports the standard escape. */
  /* Beware: tigetflag returns -1 if XT is invalid or not a boolean. */
#ifdef HAVE_USE_EXTENDED_NAMES
  use_extended_names (TRUE);
  tcapi = tigetflag("XT");
  if (tcapi == 1)
    return 1;
#endif /* HAVE_USE_EXTENDED_NAMES */

  /* Check term types that are known to support the standard escape without
   * necessarily asserting it in terminfo. */
  for (termp = known; termp; termp++)
  {
    if (term && *termp && mutt_strncasecmp (term, *termp, strlen(*termp)))
      return 1;
  }

  /* not supported */
  return 0;
}

void mutt_ts_status(char *str)
{
  /* If empty, do not set.  To clear, use a single space. */
  if (str == NULL || *str == '\0')
    return;
  fprintf(stderr, "%s%s%s", tsl, str, fsl);
}

void mutt_ts_icon(char *str)
{
  /* If empty, do not set.  To clear, use a single space. */
  if (str == NULL || *str == '\0')
    return;

  /* icon setting is not supported in terminfo, so hardcode the escape - yuck */
  fprintf(stderr, "\033]1;%s\007", str);
}

Thomas Roessler's avatar
Thomas Roessler committed
204 205
void index_make_entry (char *s, size_t l, MUTTMENU *menu, int num)
{
206
  format_flag flag = MUTT_FORMAT_ARROWCURSOR | MUTT_FORMAT_INDEX;
207
  int edgemsgno, reverse = Sort & SORT_REVERSE;
208 209
  HEADER *h = Context->hdrs[Context->v2r[num]];
  THREAD *tmp;
Thomas Roessler's avatar
Thomas Roessler committed
210 211 212

  if ((Sort & SORT_MASK) == SORT_THREADS && h->tree)
  {
213
    flag |= MUTT_FORMAT_TREE; /* display the thread tree */
Thomas Roessler's avatar
Thomas Roessler committed
214
    if (h->display_subject)
215
      flag |= MUTT_FORMAT_FORCESUBJ;
Thomas Roessler's avatar
Thomas Roessler committed
216 217
    else
    {
Thomas Roessler's avatar
Thomas Roessler committed
218 219
      if (reverse)
      {
220 221 222 223
	if (menu->top + menu->pagelen > menu->max)
	  edgemsgno = Context->v2r[menu->max - 1];
	else
	  edgemsgno = Context->v2r[menu->top + menu->pagelen - 1];
Thomas Roessler's avatar
Thomas Roessler committed
224 225
      }
      else
226
	edgemsgno = Context->v2r[menu->top];
Thomas Roessler's avatar
Thomas Roessler committed
227

228
      for (tmp = h->thread->parent; tmp; tmp = tmp->parent)
Thomas Roessler's avatar
Thomas Roessler committed
229
      {
230 231 232
	if (!tmp->message)
	  continue;

233 234
	/* if no ancestor is visible on current screen, provisionally force
	 * subject... */
235
	if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno)
Thomas Roessler's avatar
Thomas Roessler committed
236
	{
237
	  flag |= MUTT_FORMAT_FORCESUBJ;
Thomas Roessler's avatar
Thomas Roessler committed
238 239
	  break;
	}
240
	else if (tmp->message->virtual >= 0)
Thomas Roessler's avatar
Thomas Roessler committed
241 242
	  break;
      }
243
      if (flag & MUTT_FORMAT_FORCESUBJ)
Thomas Roessler's avatar
Thomas Roessler committed
244
      {
245
	for (tmp = h->thread->prev; tmp; tmp = tmp->prev)
Thomas Roessler's avatar
Thomas Roessler committed
246
	{
247 248 249
	  if (!tmp->message)
	    continue;

250
	  /* ...but if a previous sibling is available, don't force it */
251
	  if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno)
Thomas Roessler's avatar
Thomas Roessler committed
252
	    break;
253
	  else if (tmp->message->virtual >= 0)
Thomas Roessler's avatar
Thomas Roessler committed
254
	  {
255
	    flag &= ~MUTT_FORMAT_FORCESUBJ;
Thomas Roessler's avatar
Thomas Roessler committed
256 257 258 259 260 261 262
	    break;
	  }
	}
      }
    }
  }

263
  _mutt_make_string (s, l, NONULL (HdrFmt), Context, h, flag);
Thomas Roessler's avatar
Thomas Roessler committed
264 265 266 267
}

int index_color (int index_no)
{
268 269
  HEADER *h = Context->hdrs[Context->v2r[index_no]];

Mads Martin Joergensen's avatar
Mads Martin Joergensen committed
270
  if (h && h->pair)
271 272 273 274
    return h->pair;

  mutt_set_header_color (Context, h);
  return h->pair;
Thomas Roessler's avatar
Thomas Roessler committed
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
}

static int ci_next_undeleted (int msgno)
{
  int i;

  for (i=msgno+1; i < Context->vcount; i++)
    if (! Context->hdrs[Context->v2r[i]]->deleted)
      return (i);
  return (-1);
}

static int ci_previous_undeleted (int msgno)
{
  int i;

  for (i=msgno-1; i>=0; i--)
    if (! Context->hdrs[Context->v2r[i]]->deleted)
      return (i);
  return (-1);
}

/* Return the index of the first new message, or failing that, the first
 * unread message.
 */
static int ci_first_message (void)
{
  int old = -1, i;

  if (Context && Context->msgcount)
  {
    for (i=0; i < Context->vcount; i++)
    {
      if (! Context->hdrs[Context->v2r[i]]->read &&
	  ! Context->hdrs[Context->v2r[i]]->deleted)
      {
	if (! Context->hdrs[Context->v2r[i]]->old)
	  return (i);
	else if (old == -1)
	  old = i;
      }
    }
    if (old != -1)
      return (old);

    /* If Sort is reverse and not threaded, the latest message is first.
     * If Sort is threaded, the latest message is first iff exactly one
     * of Sort and SortAux are reverse.
     */
    if (((Sort & SORT_REVERSE) && (Sort & SORT_MASK) != SORT_THREADS) ||
	((Sort & SORT_MASK) == SORT_THREADS &&
	 ((Sort ^ SortAux) & SORT_REVERSE)))
      return 0;
    else
      return (Context->vcount ? Context->vcount - 1 : 0);
  }
  return 0;
}

/* This should be in mx.c, but it only gets used here. */
static int mx_toggle_write (CONTEXT *ctx)
{
  if (!ctx)
    return -1;

  if (ctx->readonly)
  {
342
    mutt_error _("Cannot toggle write on a readonly mailbox!");
Thomas Roessler's avatar
Thomas Roessler committed
343 344 345 346 347 348
    return -1;
  }

  if (ctx->dontwrite)
  {
    ctx->dontwrite = 0;
349
    mutt_message _("Changes to folder will be written on folder exit.");
Thomas Roessler's avatar
Thomas Roessler committed
350 351 352 353
  }
  else
  {
    ctx->dontwrite = 1;
354
    mutt_message _("Changes to folder will not be written.");
Thomas Roessler's avatar
Thomas Roessler committed
355 356 357 358 359
  }

  return 0;
}

360
static void update_index_threaded (CONTEXT *ctx, int check, int oldcount)
361
{
362
  HEADER **save_new = NULL;
363
  int j;
364

365 366 367
  /* save the list of new messages */
  if ((check != MUTT_REOPENED) && oldcount &&
      (ctx->pattern || option (OPTUNCOLLAPSENEW)))
368
  {
369 370 371
    save_new = (HEADER **) safe_malloc (sizeof (HEADER *) * (ctx->msgcount - oldcount));
    for (j = oldcount; j < ctx->msgcount; j++)
      save_new[j-oldcount] = ctx->hdrs[j];
372
  }
373

374 375 376 377 378 379
  /* Sort first to thread the new messages, because some patterns
   * require the threading information.
   *
   * If the mailbox was reopened, need to rethread from scratch. */
  mutt_sort_headers (ctx, (check == MUTT_REOPENED));

Thomas Roessler's avatar
Thomas Roessler committed
380
  if (ctx->pattern)
381
  {
382
    for (j = (check == MUTT_REOPENED) ? 0 : oldcount; j < ctx->msgcount; j++)
383
    {
384 385 386 387 388 389
      HEADER *h;

      if ((check != MUTT_REOPENED) && oldcount)
        h = save_new[j-oldcount];
      else
        h = ctx->hdrs[j];
Thomas Roessler's avatar
Thomas Roessler committed
390 391

      if (mutt_pattern_exec (ctx->limit_pattern,
392
			     MUTT_MATCH_FULL_ADDRESS,
393
			     ctx, h, NULL))
394
      {
395 396 397 398
        /* virtual will get properly set by mutt_set_virtual(), which
         * is called by mutt_sort_headers() just below. */
        h->virtual = 1;
        h->limited = 1;
399 400
      }
    }
401 402
    /* Need a second sort to set virtual numbers and redraw the tree */
    mutt_sort_headers (ctx, 0);
403
  }
404

405
  /* uncollapse threads with new mail */
406
  if (option(OPTUNCOLLAPSENEW))
407
  {
408
    if (check == MUTT_REOPENED)
409
    {
410
      THREAD *h, *j;
411

Thomas Roessler's avatar
Thomas Roessler committed
412
      ctx->collapsed = 0;
413

Thomas Roessler's avatar
Thomas Roessler committed
414
      for (h = ctx->tree; h; h = h->next)
415
      {
416 417
	for (j = h; !j->message; j = j->child)
	  ;
Thomas Roessler's avatar
Thomas Roessler committed
418
	mutt_uncollapse_thread (ctx, j->message);
419
      }
Thomas Roessler's avatar
Thomas Roessler committed
420
      mutt_set_virtual (ctx);
421 422 423
    }
    else if (oldcount)
    {
Thomas Roessler's avatar
Thomas Roessler committed
424
      for (j = 0; j < ctx->msgcount - oldcount; j++)
425 426 427 428 429
        if (!ctx->pattern || save_new[j]->limited)
          mutt_uncollapse_thread (ctx, save_new[j]);
      mutt_set_virtual (ctx);
    }
  }
430

431 432 433 434 435
  FREE (&save_new);
}

static void update_index_unthreaded (CONTEXT *ctx, int check, int oldcount)
{
436
  int j, padding;
437 438 439 440 441 442

  /* We are in a limited view. Check if the new message(s) satisfy
   * the limit criteria. If they do, set their virtual msgno so that
   * they will be visible in the limited view */
  if (ctx->pattern)
  {
443
    padding = mx_msg_padding_size (ctx);
444 445 446
    for (j = (check == MUTT_REOPENED) ? 0 : oldcount; j < ctx->msgcount; j++)
    {
      if (!j)
447
      {
448
	ctx->vcount = 0;
449 450
	ctx->vsize = 0;
      }
451 452 453 454 455

      if (mutt_pattern_exec (ctx->limit_pattern,
			     MUTT_MATCH_FULL_ADDRESS,
			     ctx, ctx->hdrs[j], NULL))
      {
456 457
	BODY *this_body = ctx->hdrs[j]->content;

458 459 460 461 462
	assert (ctx->vcount < ctx->msgcount);
	ctx->hdrs[j]->virtual = ctx->vcount;
	ctx->v2r[ctx->vcount] = j;
	ctx->hdrs[j]->limited = 1;
	ctx->vcount++;
463
	ctx->vsize += this_body->length + this_body->offset -
464
          this_body->hdr_offset + padding;
465 466 467
      }
    }
  }
468

469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
  /* if the mailbox was reopened, need to rethread from scratch */
  mutt_sort_headers (ctx, (check == MUTT_REOPENED));
}

static void update_index (MUTTMENU *menu, CONTEXT *ctx, int check,
			  int oldcount, int index_hint)
{
  int j;

  /* take note of the current message */
  if (oldcount)
  {
    if (menu->current < ctx->vcount)
      menu->oldcurrent = index_hint;
    else
      oldcount = 0; /* invalid message number! */
  }

  if ((Sort & SORT_MASK) == SORT_THREADS)
    update_index_threaded (ctx, check, oldcount);
  else
    update_index_unthreaded (ctx, check, oldcount);

492 493 494 495
  menu->current = -1;
  if (oldcount)
  {
    /* restore the current message to the message it was pointing to */
Thomas Roessler's avatar
Thomas Roessler committed
496
    for (j = 0; j < ctx->vcount; j++)
497
    {
Thomas Roessler's avatar
Thomas Roessler committed
498
      if (ctx->hdrs[ctx->v2r[j]]->index == menu->oldcurrent)
499 500 501 502 503 504
      {
	menu->current = j;
	break;
      }
    }
  }
505

506 507 508 509
  if (menu->current < 0)
    menu->current = ci_first_message ();
}

510 511 512
static void resort_index (MUTTMENU *menu)
{
  int i;
513
  HEADER *current = CURHDR;
514

515
  menu->current = -1;
516 517
  mutt_sort_headers (Context, 0);
  /* Restore the current message */
518

519 520 521 522 523 524 525 526
  for (i = 0; i < Context->vcount; i++)
  {
    if (Context->hdrs[Context->v2r[i]] == current)
    {
      menu->current = i;
      break;
    }
  }
527

528
  if ((Sort & SORT_MASK) == SORT_THREADS && menu->current < 0)
529
    menu->current = mutt_parent_message (Context, current, 0);
530

531 532
  if (menu->current < 0)
    menu->current = ci_first_message ();
533

534
  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
535 536
}

537
static const struct mapping_t IndexHelp[] = {
538 539 540 541 542 543 544 545
  { N_("Quit"),  OP_QUIT },
  { N_("Del"),   OP_DELETE },
  { N_("Undel"), OP_UNDELETE },
  { N_("Save"),  OP_SAVE },
  { N_("Mail"),  OP_MAIL },
  { N_("Reply"), OP_REPLY },
  { N_("Group"), OP_GROUP_REPLY },
  { N_("Help"),  OP_HELP },
546
  { NULL,	 0 }
Thomas Roessler's avatar
Thomas Roessler committed
547 548
};

549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
static void index_menu_redraw (MUTTMENU *menu)
{
  char buf[LONG_STRING];

  if (menu->redraw & REDRAW_FULL)
  {
    menu_redraw_full (menu);
    mutt_show_error ();
  }

#ifdef USE_SIDEBAR
  if (menu->redraw & REDRAW_SIDEBAR)
  {
    mutt_sb_set_buffystats (Context);
    menu_redraw_sidebar (menu);
  }
#endif

  if (Context && Context->hdrs && !(menu->current >= Context->vcount))
  {
    menu_check_recenter (menu);

    if (menu->redraw & REDRAW_INDEX)
    {
      menu_redraw_index (menu);
      menu->redraw |= REDRAW_STATUS;
    }
    else if (menu->redraw & (REDRAW_MOTION_RESYNCH | REDRAW_MOTION))
      menu_redraw_motion (menu);
    else if (menu->redraw & REDRAW_CURRENT)
      menu_redraw_current (menu);
  }

  if (menu->redraw & REDRAW_STATUS)
  {
    menu_status_line (buf, sizeof (buf), menu, NONULL (Status));
    mutt_window_move (MuttStatusWindow, 0, 0);
    SETCOLOR (MT_COLOR_STATUS);
    mutt_paddstr (MuttStatusWindow->cols, buf);
    NORMAL_COLOR;
    menu->redraw &= ~REDRAW_STATUS;
    if (option(OPTTSENABLED) && TSSupported)
    {
      menu_status_line (buf, sizeof (buf), menu, NONULL (TSStatusFormat));
      mutt_ts_status(buf);
      menu_status_line (buf, sizeof (buf), menu, NONULL (TSIconFormat));
      mutt_ts_icon(buf);
    }
  }

  menu->redraw = 0;
}

Thomas Roessler's avatar
Thomas Roessler committed
602 603 604
/* This function handles the message index window as well as commands returned
 * from the pager (MENU_PAGER).
 */
605
int mutt_index_menu (void)
Thomas Roessler's avatar
Thomas Roessler committed
606
{
607
  char buf[LONG_STRING], helpstr[LONG_STRING];
608
  int op = OP_NULL;
Thomas Roessler's avatar
Thomas Roessler committed
609 610 611 612 613 614 615 616 617 618
  int done = 0;                /* controls when to exit the "event" loop */
  int i = 0, j;
  int tag = 0;                 /* has the tag-prefix command been pressed? */
  int newcount = -1;
  int oldcount = -1;
  int rc = -1;
  MUTTMENU *menu;
  char *cp;                    /* temporary variable. */
  int index_hint;   /* used to restore cursor position */
  int do_buffy_notify = 1;
Thomas Roessler's avatar
Thomas Roessler committed
619
  int close = 0; /* did we OP_QUIT or OP_EXIT out of this menu? */
620
  int attach_msg = option(OPTATTACHMSG);
621

622
  menu = mutt_new_menu (MENU_MAIN);
Thomas Roessler's avatar
Thomas Roessler committed
623 624 625 626
  menu->make_entry = index_make_entry;
  menu->color = index_color;
  menu->current = ci_first_message ();
  menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN, IndexHelp);
627
  menu->custom_menu_redraw = index_menu_redraw;
628
  mutt_push_current_menu (menu);
629

630 631 632 633
  if (!attach_msg) {
    mutt_buffy_check(MUTT_BUFFY_CHECK_FORCE); /* force the buffy check after we
						 enter the folder */
  }
634 635 636
#ifdef USE_INOTIFY
  mutt_monitor_add (NULL);
#endif
Thomas Roessler's avatar
Thomas Roessler committed
637 638 639

  FOREVER
  {
640 641 642 643 644
    /* Clear the tag prefix unless we just started it.  Don't clear
     * the prefix on a timeout (op==-2), but do clear on an abort (op==-1)
     */
    if (tag && op != OP_TAG_PREFIX && op != OP_TAG_PREFIX_COND && op != -2)
      tag = 0;
Thomas Roessler's avatar
Thomas Roessler committed
645

Thomas Roessler's avatar
Thomas Roessler committed
646 647 648 649
    /* check if we need to resort the index because just about
     * any 'op' below could do mutt_enter_command(), either here or
     * from any new menu launched, and change $sort/$sort_aux
     */
650
    if (option (OPTNEEDRESORT) && Context && Context->msgcount && menu->current >= 0)
Thomas Roessler's avatar
Thomas Roessler committed
651
      resort_index (menu);
652 653 654 655

    menu->max = Context ? Context->vcount : 0;
    oldcount = Context ? Context->msgcount : 0;

656 657 658 659 660 661
    if (option (OPTREDRAWTREE) && Context && Context->msgcount && (Sort & SORT_MASK) == SORT_THREADS)
    {
      mutt_draw_tree (Context);
      menu->redraw |= REDRAW_STATUS;
      unset_option (OPTREDRAWTREE);
    }
Thomas Roessler's avatar
Thomas Roessler committed
662

Thomas Roessler's avatar
Thomas Roessler committed
663
    if (Context && !attach_msg)
Thomas Roessler's avatar
Thomas Roessler committed
664
    {
665
      int check;
Thomas Roessler's avatar
Thomas Roessler committed
666 667 668 669
      /* check for new mail in the mailbox.  If nonzero, then something has
       * changed about the file (either we got new mail or the file was
       * modified underneath us.)
       */
670

671
      index_hint = (Context->vcount && menu->current >= 0 && menu->current < Context->vcount) ? CURHDR->index : 0;
Thomas Roessler's avatar
Thomas Roessler committed
672

673
      if ((check = mx_check_mailbox (Context, &index_hint)) < 0)
Thomas Roessler's avatar
Thomas Roessler committed
674 675 676 677
      {
	if (!Context->path)
	{
	  /* fatal error occurred */
678
	  FREE (&Context);
Thomas Roessler's avatar
Thomas Roessler committed
679 680 681 682 683
	  menu->redraw = REDRAW_FULL;
	}

	set_option (OPTSEARCHINVALID);
      }
684
      else if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED || check == MUTT_FLAGS)
Thomas Roessler's avatar
Thomas Roessler committed
685
      {
686
	update_index (menu, Context, check, oldcount, index_hint);
687

Thomas Roessler's avatar
Thomas Roessler committed
688
	/* notify the user of new mail */
689
	if (check == MUTT_REOPENED)
690
	  mutt_error _("Mailbox was externally modified.  Flags may be wrong.");
691
	else if (check == MUTT_NEW_MAIL)
Thomas Roessler's avatar
Thomas Roessler committed
692
	{
693
	  mutt_message _("New mail in this mailbox.");
Thomas Roessler's avatar
Thomas Roessler committed
694 695
	  if (option (OPTBEEPNEW))
	    beep ();
696 697 698 699 700 701
	  if (NewMailCmd)
	  {
	    char cmd[LONG_STRING];
	    menu_status_line(cmd, sizeof(cmd), menu, NONULL(NewMailCmd));
	    mutt_system(cmd);
	  }
702
	} else if (check == MUTT_FLAGS)
703 704
	  mutt_message _("Mailbox was externally modified.");

Thomas Roessler's avatar
Thomas Roessler committed
705 706
	/* avoid the message being overwritten by buffy */
	do_buffy_notify = 0;
707

Thomas Roessler's avatar
Thomas Roessler committed
708 709
	menu->redraw = REDRAW_FULL;
	menu->max = Context->vcount;
710

Thomas Roessler's avatar
Thomas Roessler committed
711 712
	set_option (OPTSEARCHINVALID);
      }
713 714
    }

Thomas Roessler's avatar
Thomas Roessler committed
715
    if (!attach_msg)
Thomas Roessler's avatar
Thomas Roessler committed
716
    {
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
      /* check for new mail in the incoming folders */
      oldcount = newcount;
      if ((newcount = mutt_buffy_check (0)) != oldcount)
        menu->redraw |= REDRAW_STATUS;
      if (do_buffy_notify)
      {
        if (mutt_buffy_notify())
        {
          menu->redraw |= REDRAW_STATUS;
          if (option (OPTBEEPNEW))
            beep();
          if (NewMailCmd)
          {
            char cmd[LONG_STRING];
            menu_status_line(cmd, sizeof(cmd), menu, NONULL(NewMailCmd));
            mutt_system(cmd);
          }
        }
      }
      else
        do_buffy_notify = 1;
Thomas Roessler's avatar
Thomas Roessler committed
738 739
    }

740
    if (op >= 0)
741
      mutt_curs_set (0);
Thomas Roessler's avatar
Thomas Roessler committed
742 743 744

    if (menu->menu == MENU_MAIN)
    {
745
      index_menu_redraw (menu);
Thomas Roessler's avatar
Thomas Roessler committed
746

747 748 749 750 751 752 753
      /* give visual indication that the next command is a tag- command */
      if (tag)
      {
        mutt_window_mvaddstr (MuttMessageWindow, 0, 0, "tag-");
        mutt_window_clrtoeol (MuttMessageWindow);
      }

754
      if (menu->current < menu->max)
755
        menu->oldcurrent = menu->current;
756
      else
757
        menu->oldcurrent = -1;
Thomas Roessler's avatar
Thomas Roessler committed
758 759

      if (option (OPTARROWCURSOR))
760
        mutt_window_move (MuttIndexWindow, menu->current - menu->top + menu->offset, 2);
761
      else if (option (OPTBRAILLEFRIENDLY))
762
        mutt_window_move (MuttIndexWindow, menu->current - menu->top + menu->offset, 0);
Thomas Roessler's avatar
Thomas Roessler committed
763
      else
764
        mutt_window_move (MuttIndexWindow, menu->current - menu->top + menu->offset,
765
                          MuttIndexWindow->cols - 1);
Thomas Roessler's avatar
Thomas Roessler committed
766 767 768
      mutt_refresh ();

#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
769
      if (SigWinch)
Thomas Roessler's avatar
Thomas Roessler committed
770 771 772
      {
	mutt_flushinp ();
	mutt_resize_screen ();
773
	SigWinch = 0;
Thomas Roessler's avatar
Thomas Roessler committed
774
	menu->top = 0; /* so we scroll the right amount */
775 776 777 778 779
	/*
	 * force a real complete redraw.  clrtobot() doesn't seem to be able
	 * to handle every case without this.
	 */
	clearok(stdscr,TRUE);
Thomas Roessler's avatar
Thomas Roessler committed
780 781 782 783
	continue;
      }
#endif

784 785 786 787
      op = km_dokey (MENU_MAIN);

      dprint(4, (debugfile, "mutt_index_menu[%d]: Got op %d\n", __LINE__, op));

788
      /* either user abort or timeout */
789
      if (op < 0)
790 791 792 793 794
      {
        if (tag)
          mutt_window_clearline (MuttMessageWindow, 0);
        continue;
      }
795

796
      mutt_curs_set (1);
797

Thomas Roessler's avatar
Thomas Roessler committed
798
      /* special handling for the tag-prefix function */
799
      if (op == OP_TAG_PREFIX || op == OP_TAG_PREFIX_COND)
Thomas Roessler's avatar
Thomas Roessler committed
800
      {
801 802 803 804
        /* A second tag-prefix command aborts */
        if (tag)
        {
          tag = 0;
805
          mutt_window_clearline (MuttMessageWindow, 0);
806 807
          continue;
        }
Thomas Roessler's avatar
Thomas Roessler committed
808

809 810 811 812 813 814 815 816
	if (!Context)
	{
	  mutt_error _("No mailbox is open.");
	  continue;
	}

	if (!Context->tagged)
	{
817 818 819 820 821 822 823
          if (op == OP_TAG_PREFIX)
            mutt_error _("No tagged messages.");
          else if (op == OP_TAG_PREFIX_COND)
          {
            mutt_flush_macro_to_endcond ();
            mutt_message  _("Nothing to do.");
          }
824 825 826
	  continue;
	}

827 828 829
        /* get the real command */
        tag = 1;
        continue;
830
      }
831 832
      else if (option (OPTAUTOTAG) && Context && Context->tagged)
	tag = 1;
833

Thomas Roessler's avatar
Thomas Roessler committed
834 835 836
      mutt_clear_error ();
    }
    else
837 838 839 840 841
    {
      if (menu->current < menu->max)
	menu->oldcurrent = menu->current;
      else
	menu->oldcurrent = -1;
842

Thomas Roessler's avatar
Thomas Roessler committed
843
      mutt_curs_set (1);	/* fallback from the pager */
844
    }
Thomas Roessler's avatar
Thomas Roessler committed
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898

    switch (op)
    {

      /* ----------------------------------------------------------------------
       * movement commands
       */

      case OP_BOTTOM_PAGE:
	menu_bottom_page (menu);
	break;
      case OP_FIRST_ENTRY:
	menu_first_entry (menu);
	break;
      case OP_MIDDLE_PAGE:
	menu_middle_page (menu);
	break;
      case OP_HALF_UP:
	menu_half_up (menu);
	break;
      case OP_HALF_DOWN:
	menu_half_down (menu);
	break;
      case OP_NEXT_LINE:
	menu_next_line (menu);
	break;
      case OP_PREV_LINE:
	menu_prev_line (menu);
	break;
      case OP_NEXT_PAGE:
	menu_next_page (menu);
	break;
      case OP_PREV_PAGE:
	menu_prev_page (menu);
	break;
      case OP_LAST_ENTRY:
	menu_last_entry (menu);
	break;
      case OP_TOP_PAGE:
	menu_top_page (menu);
	break;
      case OP_CURRENT_TOP:
	menu_current_top (menu);
	break;
      case OP_CURRENT_MIDDLE:
	menu_current_middle (menu);
	break;
      case OP_CURRENT_BOTTOM:
	menu_current_bottom (menu);
	break;

      case OP_JUMP:

	CHECK_MSGCOUNT;
899
        CHECK_VISIBLE;
900
        if (isdigit (LastKey)) mutt_unget_event (LastKey, 0);
Thomas Roessler's avatar
Thomas Roessler committed
901
	buf[0] = 0;
902 903
	if (mutt_get_field (_("Jump to message: "), buf, sizeof (buf), 0) != 0
	    || !buf[0])
904 905 906 907 908 909
        {
          if (menu->menu == MENU_PAGER)
          {
            op = OP_DISPLAY_MESSAGE;
            continue;
          }
Thomas Roessler's avatar
Thomas Roessler committed
910
	  break;
911
        }
Thomas Roessler's avatar
Thomas Roessler committed
912

913
	if (mutt_atoi (buf, &i) < 0)
Thomas Roessler's avatar
Thomas Roessler committed
914
	{
915
	  mutt_error _("Argument must be a message number.");
Thomas Roessler's avatar
Thomas Roessler committed
916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
	  break;
	}

	if (i > 0 && i <= Context->msgcount)
	{
	  for (j = i-1; j < Context->msgcount; j++)
	  {
	    if (Context->hdrs[j]->virtual != -1)
	      break;
	  }
	  if (j >= Context->msgcount)
	  {
	    for (j = i-2; j >= 0; j--)
	    {
	      if (Context->hdrs[j]->virtual != -1)
		break;
	    }
	  }

	  if (j >= 0)
	  {
	    menu->current = Context->hdrs[j]->virtual;
	    if (menu->menu == MENU_PAGER)
	    {
	      op = OP_DISPLAY_MESSAGE;
	      continue;
	    }
	    else
944
              menu->redraw = REDRAW_MOTION;
Thomas Roessler's avatar
Thomas Roessler committed
945 946
	  }
	  else
947
	    mutt_error _("That message is not visible.");
Thomas Roessler's avatar
Thomas Roessler committed
948 949
	}
	else
950
	  mutt_error _("Invalid message number.");
Thomas Roessler's avatar
Thomas Roessler committed
951 952 953 954 955 956 957 958 959 960

	break;

	/* --------------------------------------------------------------------
	 * `index' specific commands
	 */

      case OP_MAIN_DELETE_PATTERN:

	CHECK_MSGCOUNT;
961
        CHECK_VISIBLE;
Thomas Roessler's avatar
Thomas Roessler committed
962
	CHECK_READONLY;
963
        /* L10N: CHECK_ACL */
964
	CHECK_ACL(MUTT_ACL_DELETE, _("Cannot delete message(s)"));
965

966
	CHECK_ATTACH;
967
	mutt_pattern_func (MUTT_DELETE, _("Delete messages matching: "));
968
	menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
Thomas Roessler's avatar
Thomas Roessler committed
969 970 971 972 973
	break;

#ifdef USE_POP
      case OP_MAIN_FETCH_MAIL:

974
	CHECK_ATTACH;
975
	pop_fetch_mail ();
Thomas Roessler's avatar
Thomas Roessler committed
976 977 978 979 980 981 982 983 984 985
	menu->redraw = REDRAW_FULL;
	break;
#endif /* USE_POP */

      case OP_HELP:

	mutt_help (MENU_MAIN);
	menu->redraw = REDRAW_FULL;
	break;

986 987 988 989 990 991
      case OP_ERROR_HISTORY:

	mutt_error_history_display ();
	menu->redraw = REDRAW_FULL;
	break;

Thomas Roessler's avatar
Thomas Roessler committed
992
      case OP_MAIN_SHOW_LIMIT:
993
	CHECK_IN_MAILBOX;
Thomas Roessler's avatar
Thomas Roessler committed
994
	if (!Context->pattern)
995
          mutt_message _("No limit pattern is in effect.");
Thomas Roessler's avatar
Thomas Roessler committed
996 997
	else
	{
998 999 1000 1001
          char buf[STRING];
          /* L10N: ask for a limit to apply */
          snprintf (buf, sizeof(buf), _("Limit: %s"),Context->pattern);
          mutt_message ("%s", buf);
Thomas Roessler's avatar
Thomas Roessler committed
1002 1003 1004 1005 1006
	}
        break;

      case OP_MAIN_LIMIT:

1007
	CHECK_IN_MAILBOX;
1008
	menu->oldcurrent = (Context->vcount && menu->current >= 0 && menu->current < Context->vcount) ?
1009
          CURHDR->index : -1;
1010
	if (mutt_pattern_func (MUTT_LIMIT, _("Limit to messages matching: ")) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
	{
	  if (menu->oldcurrent >= 0)
	  {
	    /* try to find what used to be the current message */
	    menu->current = -1;
	    for (i = 0; i < Context->vcount; i++)
	      if (Context->hdrs[Context->v2r[i]]->index == menu->oldcurrent)
	      {
		menu->current = i;
		break;
	      }
	    if (menu->current < 0) menu->current = 0;
	  }
	  else
	    menu->current = 0;
1026
	  if (Context->msgcount && (Sort & SORT_MASK) == SORT_THREADS)
1027
	    mutt_draw_tree (Context);
1028
	  menu->redraw = REDRAW_FULL;
Thomas Roessler's avatar
Thomas Roessler committed
1029
	}
1030 1031
        if (Context->pattern)
	  mutt_message _("To view all messages, limit to \"all\".");
1032
	break;
Thomas Roessler's avatar
Thomas Roessler committed
1033 1034 1035

      case OP_QUIT:

Thomas Roessler's avatar
Thomas Roessler committed
1036 1037 1038
	close = op;
	if (attach_msg)
	{
1039 1040
          done = 1;
          break;
Thomas Roessler's avatar
Thomas Roessler committed
1041 1042
	}

1043
	if (query_quadoption (OPT_QUIT, _("Quit Mutt?")) == MUTT_YES)
1044 1045
	{
	  int check;
1046

1047
	  oldcount = Context ? Context->msgcount : 0;
1048 1049

	  if (!Context || (check = mx_close_mailbox (Context, &index_hint)) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
1050 1051
	    done = 1;
	  else
1052
	  {
1053
	    if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED)
1054 1055
	      update_index (menu, Context, check, oldcount, index_hint);

Thomas Roessler's avatar
Thomas Roessler committed
1056
	    menu->redraw = REDRAW_FULL; /* new mail arrived? */
1057 1058
	    set_option (OPTSEARCHINVALID);
	  }
Thomas Roessler's avatar
Thomas Roessler committed
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
	}
	break;

      case OP_REDRAW:

	clearok (stdscr, TRUE);
	menu->redraw = REDRAW_FULL;
	break;

      case OP_SEARCH:
      case OP_SEARCH_REVERSE:
      case OP_SEARCH_NEXT:
      case OP_SEARCH_OPPOSITE:

	CHECK_MSGCOUNT;
1074
        CHECK_VISIBLE;
1075
	if ((menu->current = mutt_search_command (menu->current, op)) == -1)
Thomas Roessler's avatar
Thomas Roessler committed
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
	  menu->current = menu->oldcurrent;
	else
	  menu->redraw = REDRAW_MOTION;
	break;

      case OP_SORT:
      case OP_SORT_REVERSE:

	if (mutt_select_sort ((op == OP_SORT_REVERSE)) == 0)
	{
	  if (Context && Context->msgcount)
	  {
1088
	    resort_index (menu);
Thomas Roessler's avatar
Thomas Roessler committed
1089 1090
	    set_option (OPTSEARCHINVALID);
	  }
1091 1092 1093 1094 1095
	  if (menu->menu == MENU_PAGER)
	  {
	    op = OP_DISPLAY_MESSAGE;
	    continue;
	  }
1096
	  menu->redraw |= REDRAW_STATUS;
Thomas Roessler's avatar
Thomas Roessler committed
1097 1098 1099 1100 1101 1102
	}
	break;

      case OP_TAG:

	CHECK_MSGCOUNT;
1103
        CHECK_VISIBLE;
Thomas Roessler's avatar
Thomas Roessler committed
1104 1105 1106
	if (tag && !option (OPTAUTOTAG))
	{
	  for (j = 0; j < Context->vcount; j++)
1107
	    mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], MUTT_TAG, 0);
1108
	  menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
Thomas Roessler's avatar
Thomas Roessler committed
1109 1110 1111
	}
	else
	{
1112
	  mutt_set_flag (Context, CURHDR, MUTT_TAG, !CURHDR->tagged);
1113 1114 1115 1116 1117

	  Context->last_tag = CURHDR->tagged ? CURHDR :
	    ((Context->last_tag == CURHDR && !CURHDR->tagged)
	     ? NULL : Context->last_tag);

1118
	  menu->redraw |= REDRAW_STATUS;
Thomas Roessler's avatar
Thomas Roessler committed
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
	  if (option (OPTRESOLVE) && menu->current < Context->vcount - 1)
	  {
	    menu->current++;
	    menu->redraw |= REDRAW_MOTION_RESYNCH;
	  }
	  else
	    menu->redraw |= REDRAW_CURRENT;
	}
	break;

      case OP_MAIN_TAG_PATTERN:

	CHECK_MSGCOUNT;
1132
        CHECK_VISIBLE;
1133
	mutt_pattern_func (MUTT_TAG, _("Tag messages matching: "));
1134
	menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
Thomas Roessler's avatar
Thomas Roessler committed
1135 1136 1137 1138 1139
	break;

      case OP_MAIN_UNDELETE_PATTERN:

	CHECK_MSGCOUNT;
1140
        CHECK_VISIBLE;
Thomas Roessler's avatar
Thomas Roessler committed
1141
	CHECK_READONLY;
1142
        /* L10N: CHECK_ACL */
1143
	CHECK_ACL(MUTT_ACL_DELETE, _("Cannot undelete message(s)"));
1144

1145
	if (mutt_pattern_func (MUTT_UNDELETE, _("Undelete messages matching: ")) == 0)
1146
	  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
Thomas Roessler's avatar
Thomas Roessler committed
1147 1148 1149 1150 1151
	break;

      case OP_MAIN_UNTAG_PATTERN:

	CHECK_MSGCOUNT;
1152
        CHECK_VISIBLE;
1153
	if (mutt_pattern_func (MUTT_UNTAG, _("Untag messages matching: ")) == 0)
1154
	  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
Thomas Roessler's avatar
Thomas Roessler committed
1155 1156 1157 1158 1159 1160
	break;

	/* --------------------------------------------------------------------
	 * The following operations can be performed inside of the pager.
	 */

1161 1162
#ifdef USE_IMAP
      case OP_MAIN_IMAP_FETCH:
1163
	if (Context && Context->magic == MUTT_IMAP)
1164
	  imap_check_mailbox (Context, &index_hint, 1);
1165
        break;
1166 1167

      case OP_MAIN_IMAP_LOGOUT_ALL:
1168
	if (Context && Context->magic == MUTT_IMAP)
1169
	{
1170 1171 1172
          int check;

	  if ((check = mx_close_mailbox (Context, &index_hint)) != 0)
1173
	  {
1174 1175
            if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED)
              update_index (menu, Context, check, oldcount, index_hint);
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
	    set_option (OPTSEARCHINVALID);
	    menu->redraw = REDRAW_FULL;
	    break;
	  }
	  FREE (&Context);
	}
	imap_logout_all();
	mutt_message _("Logged out of IMAP servers.");
	set_option (OPTSEARCHINVALID);
	menu->redraw = REDRAW_FULL;
	break;
1187
#endif
1188

1189 1190
      case OP_MAIN_SYNC_FOLDER:

1191 1192 1193
	if (Context && !Context->msgcount)
	  break;

1194 1195 1196 1197 1198
	CHECK_MSGCOUNT;
	CHECK_READONLY;
	{
	  int oldvcount = Context->vcount;
	  int oldcount  = Context->msgcount;
1199 1200 1201
	  int check, newidx;
	  HEADER *newhdr = NULL;

1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
	  /* don't attempt to move the cursor if there are no visible messages in the current limit */
	  if (menu->current < Context->vcount)
	  {
	    /* threads may be reordered, so figure out what header the cursor
	     * should be on. #3092 */
	    newidx = menu->current;
	    if (CURHDR->deleted)
	      newidx = ci_next_undeleted (menu->current);
	    if (newidx < 0)
	      newidx = ci_previous_undeleted (menu->current);
	    if (newidx >= 0)
	      newhdr = Context->hdrs[Context->v2r[newidx]];
	  }
1215 1216 1217

	  if ((check = mx_sync_mailbox (Context, &index_hint)) == 0)
	  {
1218 1219 1220 1221 1222 1223 1224 1225 1226
	    if (newhdr && Context->vcount != oldvcount)
	      for (j = 0; j < Context->vcount; j++)
	      {
		if (Context->hdrs[Context->v2r[j]] == newhdr)
		{
		  menu->current = j;
		  break;
		}
	      }
1227 1228
	    set_option (OPTSEARCHINVALID);
	  }
1229
	  else if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED)
1230 1231
	    update_index (menu, Context, check, oldcount, index_hint);

1232
	  /*
1233 1234 1235 1236 1237 1238 1239 1240 1241
	   * do a sanity check even if mx_sync_mailbox failed.
	   */

	  if (menu->current < 0 || menu->current >= Context->vcount)
	    menu->current = ci_first_message ();
	}

	/* check for a fatal error, or all messages deleted */
	if (!Context->path)
1242
	  FREE (&Context);
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253

	/* if we were in the pager, redisplay the message */
	if (menu->menu == MENU_PAGER)
	{
	  op = OP_DISPLAY_MESSAGE;
	  continue;
	}
        else
	  menu->redraw = REDRAW_FULL;
	break;

1254 1255 1256
#ifdef USE_SIDEBAR
      case OP_SIDEBAR_OPEN:
#endif
Thomas Roessler's avatar
Thomas Roessler committed
1257
      case OP_MAIN_CHANGE_FOLDER:
1258
      case OP_MAIN_NEXT_UNREAD_MAILBOX:
1259

Thomas Roessler's avatar
Thomas Roessler committed
1260
	if (attach_msg)
Thomas Roessler's avatar
Thomas Roessler committed
1261 1262 1263 1264 1265 1266
	  op = OP_MAIN_CHANGE_FOLDER_READONLY;

	/* fallback to the readonly case */

      case OP_MAIN_CHANGE_FOLDER_READONLY:

Thomas Roessler's avatar
Thomas Roessler committed
1267
        if ((op == OP_MAIN_CHANGE_FOLDER_READONLY) || option (OPTREADONLY))
1268
          cp = _("Open mailbox in read-only mode");
Thomas Roessler's avatar
Thomas Roessler committed
1269 1270
        else
          cp = _("Open mailbox");
Thomas Roessler's avatar
Thomas Roessler committed
1271 1272

	buf[0] = '\0';
1273
	if ((op == OP_MAIN_NEXT_UNREAD_MAILBOX) && Context && Context->path)
Thomas Roessler's avatar
Thomas Roessler committed
1274
	{
N.J. Mann's avatar
N.J. Mann committed
1275
	  strfcpy (buf, Context->path, sizeof (buf));
1276
	  mutt_pretty_mailbox (buf, sizeof (buf));
N.J. Mann's avatar
N.J. Mann committed
1277 1278 1279
	  mutt_buffy (buf, sizeof (buf));
	  if (!buf[0])
	  {
1280
	    mutt_error _("No mailboxes have new mail");
N.J. Mann's avatar
N.J. Mann committed
1281 1282 1283
	    break;
	  }
	}
1284 1285 1286
#ifdef USE_SIDEBAR
        else if (op == OP_SIDEBAR_OPEN)
        {
1287
          const char *path = mutt_sb_get_highlight();
1288 1289 1290 1291 1292
          if (!path || !*path)
            break;
          strncpy (buf, path, sizeof (buf));
        }
#endif
Brendan Cully's avatar
Brendan Cully committed
1293
	else
N.J. Mann's avatar
N.J. Mann committed
1294
	{
1295 1296 1297 1298 1299
          if (option (OPTCHANGEFOLDERNEXT) && Context && Context->path)
          {
            strfcpy (buf, Context->path, sizeof (buf));
            mutt_pretty_mailbox (buf, sizeof (buf));
          }
N.J. Mann's avatar
N.J. Mann committed
1300 1301
	  mutt_buffy (buf, sizeof (buf));

1302
          if (mutt_enter_fname (cp, buf, sizeof (buf), 1) == -1)
1303 1304 1305 1306 1307 1308 1309 1310 1311
          {
            if (menu->menu == MENU_PAGER)
            {
              op = OP_DISPLAY_MESSAGE;
              continue;
            }
            else
              break;
          }
N.J. Mann's avatar
N.J. Mann committed
1312 1313
	  if (!buf[0])
	  {
1314
            mutt_window_clearline (MuttMessageWindow, 0);
N.J. Mann's avatar
N.J. Mann committed
1315 1316
	    break;
	  }
Thomas Roessler's avatar
Thomas Roessler committed
1317 1318 1319 1320 1321
	}

	mutt_expand_path (buf, sizeof (buf));
	if (mx_get_magic (buf) <= 0)
	{
1322
	  mutt_error (_("%s is not a mailbox."), buf);
Thomas Roessler's avatar
Thomas Roessler committed
1323 1324 1325
	  break;
	}

1326 1327 1328 1329
	/* keepalive failure in mutt_enter_fname may kill connection. #3028 */
	if (Context && !Context->path)
	  FREE (&Context);

Thomas Roessler's avatar
Thomas Roessler committed
1330 1331
        if (Context)
        {
1332
	  int check;
1333
          char *new_last_folder;
1334 1335
#ifdef USE_INOTIFY
          int monitor_remove_rc;
1336

1337 1338
          monitor_remove_rc = mutt_monitor_remove (NULL);
#endif
1339 1340
#ifdef USE_COMPRESSED
	  if (Context->compress_info && Context->realpath)
1341
	    new_last_folder = safe_strdup (Context->realpath);
1342 1343
	  else
#endif
1344
            new_last_folder = safe_strdup (Context->path);
1345
	  oldcount = Context ? Context->msgcount : 0;
Thomas Roessler's avatar
Thomas Roessler committed
1346

1347
	  if ((check = mx_close_mailbox (Context, &index_hint)) != 0)
Thomas Roessler's avatar
Thomas Roessler committed
1348
	  {
1349 1350 1351 1352
#ifdef USE_INOTIFY
            if (!monitor_remove_rc)
              mutt_monitor_add (NULL);
#endif
1353
	    if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED)
1354
	      update_index (menu, Context, check, oldcount, index_hint);
1355

1356
            FREE (&new_last_folder);
1357
	    set_option (OPTSEARCHINVALID);
1358
	    menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
Thomas Roessler's avatar
Thomas Roessler committed
1359 1360
	    break;
	  }
1361
	  FREE (&Context);
1362 1363
          FREE (&LastFolder);
          LastFolder = new_last_folder;
Thomas Roessler's avatar
Thomas Roessler committed
1364
	}
1365
	mutt_str_replace (&CurrentFolder, buf);
Thomas Roessler's avatar
Thomas Roessler committed
1366

1367
        mutt_sleep (0);
1368

1369 1370 1371 1372 1373 1374 1375 1376
        /* Note that menu->menu may be MENU_PAGER if the change folder
         * operation originated from the pager.
         *
         * However, exec commands currently use CurrentMenu to determine what
         * functions are available, which is automatically set by the
         * mutt_push/pop_current_menu() functions.  If that changes, the menu
         * would need to be reset here, and the pager cleanup code after the
         * switch statement would need to be run. */
Thomas Roessler's avatar
Thomas Roessler committed
1377 1378
	mutt_folder_hook (buf);

1379
	if ((Context = mx_open_mailbox (buf,
Thomas Roessler's avatar
Thomas Roessler committed
1380
					(option (OPTREADONLY) || op == OP_MAIN_CHANGE_FOLDER_READONLY) ?
1381
					MUTT_READONLY : 0, NULL)) != NULL)
Thomas Roessler's avatar
Thomas Roessler committed
1382 1383
	{
	  menu->current = ci_first_message ();
1384 1385 1386
#ifdef USE_INOTIFY
          mutt_monitor_add (NULL);
#endif
Thomas Roessler's avatar
Thomas Roessler committed
1387 1388 1389 1390
	}
	else
	  menu->current = 0;

1391 1392 1393 1394
#ifdef USE_SIDEBAR
        mutt_sb_set_open_buffy ();
#endif

Thomas Roessler's avatar
Thomas Roessler committed
1395
	mutt_clear_error ();
1396 1397 1398
	mutt_buffy_check(MUTT_BUFFY_CHECK_FORCE); /* force the buffy check after
						     we have changed the
						     folder */
Thomas Roessler's avatar
Thomas Roessler committed
1399 1400 1401 1402 1403 1404 1405 1406
	menu->redraw = REDRAW_FULL;
	set_option (OPTSEARCHINVALID);
	break;

      case OP_DISPLAY_MESSAGE:
      case OP_DISPLAY_HEADERS: /* don't weed the headers */

	CHECK_MSGCOUNT;
1407
        CHECK_VISIBLE;
Thomas Roessler's avatar
Thomas Roessler committed
1408 1409 1410 1411 1412 1413 1414 1415
	/*
	 * toggle the weeding of headers so that a user can press the key
	 * again while reading the message.
	 */
	if (op == OP_DISPLAY_HEADERS)
	  toggle_option (OPTWEED);

	unset_option (OPTNEEDRESORT);
1416

1417 1418 1419 1420
	if ((Sort & SORT_MASK) == SORT_THREADS && CURHDR->collapsed)
	{
	  mutt_uncollapse_thread (Context, CURHDR);
	  mutt_set_virtual (Context);
1421 1422
	  if (option (OPTUNCOLLAPSEJUMP))
	    menu->current = mutt_thread_next_unread (Context, CURHDR);
1423
	}
1424 1425

	if (option (OPTPGPAUTODEC) && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED)))
1426
	  mutt_check_traditional_pgp (tag ? NULL : CURHDR, &