curs_main.c 57.2 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
#include "mutt_crypt.h"
Thomas Roessler's avatar
Thomas Roessler committed
45 46 47 48 49 50 51 52 53 54


#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
55 56
#include <assert.h>

Thomas Roessler's avatar
Thomas Roessler committed
57 58 59 60
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.");
61
static const char *No_visible = N_("No visible messages.");
62

63 64 65 66 67 68 69
#define CHECK_IN_MAILBOX if (!Context) \
	{ \
		mutt_flushinp (); \
		mutt_error _(No_mailbox_is_open); \
		break; \
	}

Thomas Roessler's avatar
Thomas Roessler committed
70 71 72
#define CHECK_MSGCOUNT if (!Context) \
	{ \
	  	mutt_flushinp (); \
73
		mutt_error _(No_mailbox_is_open); \
Thomas Roessler's avatar
Thomas Roessler committed
74 75 76 77 78
		break; \
	} \
	else if (!Context->msgcount) \
	{ \
	  	mutt_flushinp (); \
79
		mutt_error _(There_are_no_messages); \
Thomas Roessler's avatar
Thomas Roessler committed
80 81 82
		break; \
	}

83 84 85 86 87 88
#define CHECK_VISIBLE if (Context && menu->current >= Context->vcount) \
  	{\
	  	mutt_flushinp (); \
	  	mutt_error _(No_visible); \
	  	break; \
	}
89

90

Thomas Roessler's avatar
Thomas Roessler committed
91 92 93
#define CHECK_READONLY if (Context->readonly) \
			{ \
			  	mutt_flushinp (); \
94
				mutt_error _(Mailbox_is_read_only); \
Thomas Roessler's avatar
Thomas Roessler committed
95 96 97
				break; \
			}

Rocco Rutte's avatar
Rocco Rutte committed
98 99
#define CHECK_ACL(aclbit,action) \
		if (!mutt_bit_isset(Context->rights,aclbit)) { \
100
			mutt_flushinp(); \
101 102
        /* L10N: %s is one of the CHECK_ACL entries below. */ \
			mutt_error (_("%s: Operation not permitted by ACL"), action); \
103 104 105
			break; \
		}

106 107 108
#define CHECK_ATTACH if(option(OPTATTACHMSG)) \
		     {\
			mutt_flushinp (); \
109
			mutt_error _(Function_not_permitted_in_attach_message_mode); \
110 111 112
			break; \
		     }

Thomas Roessler's avatar
Thomas Roessler committed
113 114
#define CURHDR Context->hdrs[Context->v2r[menu->current]]
#define OLDHDR Context->hdrs[Context->v2r[menu->oldcurrent]]
115
#define UNREAD(h) mutt_thread_contains_unread (Context, h)
Thomas Roessler's avatar
Thomas Roessler committed
116

117 118 119 120 121 122 123 124 125 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
/* 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
194 195
void index_make_entry (char *s, size_t l, MUTTMENU *menu, int num)
{
196
  format_flag flag = MUTT_FORMAT_MAKEPRINT | MUTT_FORMAT_ARROWCURSOR | MUTT_FORMAT_INDEX;
197
  int edgemsgno, reverse = Sort & SORT_REVERSE;
198 199
  HEADER *h = Context->hdrs[Context->v2r[num]];
  THREAD *tmp;
Thomas Roessler's avatar
Thomas Roessler committed
200 201 202

  if ((Sort & SORT_MASK) == SORT_THREADS && h->tree)
  {
203
    flag |= MUTT_FORMAT_TREE; /* display the thread tree */
Thomas Roessler's avatar
Thomas Roessler committed
204
    if (h->display_subject)
205
      flag |= MUTT_FORMAT_FORCESUBJ;
Thomas Roessler's avatar
Thomas Roessler committed
206 207
    else
    {
Thomas Roessler's avatar
Thomas Roessler committed
208 209
      if (reverse)
      {
210 211 212 213
	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
214 215
      }
      else
216
	edgemsgno = Context->v2r[menu->top];
Thomas Roessler's avatar
Thomas Roessler committed
217

218
      for (tmp = h->thread->parent; tmp; tmp = tmp->parent)
Thomas Roessler's avatar
Thomas Roessler committed
219
      {
220 221 222
	if (!tmp->message)
	  continue;

223 224
	/* if no ancestor is visible on current screen, provisionally force
	 * subject... */
225
	if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno)
Thomas Roessler's avatar
Thomas Roessler committed
226
	{
227
	  flag |= MUTT_FORMAT_FORCESUBJ;
Thomas Roessler's avatar
Thomas Roessler committed
228 229
	  break;
	}
230
	else if (tmp->message->virtual >= 0)
Thomas Roessler's avatar
Thomas Roessler committed
231 232
	  break;
      }
233
      if (flag & MUTT_FORMAT_FORCESUBJ)
Thomas Roessler's avatar
Thomas Roessler committed
234
      {
235
	for (tmp = h->thread->prev; tmp; tmp = tmp->prev)
Thomas Roessler's avatar
Thomas Roessler committed
236
	{
237 238 239
	  if (!tmp->message)
	    continue;

240
	  /* ...but if a previous sibling is available, don't force it */
241
	  if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno)
Thomas Roessler's avatar
Thomas Roessler committed
242
	    break;
243
	  else if (tmp->message->virtual >= 0)
Thomas Roessler's avatar
Thomas Roessler committed
244
	  {
245
	    flag &= ~MUTT_FORMAT_FORCESUBJ;
Thomas Roessler's avatar
Thomas Roessler committed
246 247 248 249 250 251 252
	    break;
	  }
	}
      }
    }
  }

253
  _mutt_make_string (s, l, NONULL (HdrFmt), Context, h, flag);
Thomas Roessler's avatar
Thomas Roessler committed
254 255 256 257
}

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

Mads Martin Joergensen's avatar
Mads Martin Joergensen committed
260
  if (h && h->pair)
261 262 263 264
    return h->pair;

  mutt_set_header_color (Context, h);
  return h->pair;
Thomas Roessler's avatar
Thomas Roessler committed
265 266 267 268 269 270 271 272 273 274 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
}

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)
  {
332
    mutt_error _("Cannot toggle write on a readonly mailbox!");
Thomas Roessler's avatar
Thomas Roessler committed
333 334 335 336 337 338
    return -1;
  }

  if (ctx->dontwrite)
  {
    ctx->dontwrite = 0;
339
    mutt_message _("Changes to folder will be written on folder exit.");
Thomas Roessler's avatar
Thomas Roessler committed
340 341 342 343
  }
  else
  {
    ctx->dontwrite = 1;
344
    mutt_message _("Changes to folder will not be written.");
Thomas Roessler's avatar
Thomas Roessler committed
345 346 347 348 349
  }

  return 0;
}

350 351 352 353 354 355
static void update_index (MUTTMENU *menu, CONTEXT *ctx, int check,
			  int oldcount, int index_hint)
{
  /* store pointers to the newly added messages */
  HEADER  **save_new = NULL;
  int j;
356

357 358 359
  /* take note of the current message */
  if (oldcount)
  {
Thomas Roessler's avatar
Thomas Roessler committed
360
    if (menu->current < ctx->vcount)
361 362 363 364
      menu->oldcurrent = index_hint;
    else
      oldcount = 0; /* invalid message number! */
  }
365

366 367 368
  /* 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 */
Thomas Roessler's avatar
Thomas Roessler committed
369
  if (ctx->pattern)
370
  {
Thomas Roessler's avatar
Thomas Roessler committed
371
#define THIS_BODY ctx->hdrs[j]->content
372
    for (j = (check == MUTT_REOPENED) ? 0 : oldcount; j < ctx->msgcount; j++)
373
    {
Thomas Roessler's avatar
Thomas Roessler committed
374 375 376 377
      if (!j)
	ctx->vcount = 0;

      if (mutt_pattern_exec (ctx->limit_pattern,
378
			     MUTT_MATCH_FULL_ADDRESS,
Thomas Roessler's avatar
Thomas Roessler committed
379
			     ctx, ctx->hdrs[j]))
380
      {
Thomas Roessler's avatar
Thomas Roessler committed
381 382 383 384 385 386
	assert (ctx->vcount < ctx->msgcount);
	ctx->hdrs[j]->virtual = ctx->vcount;
	ctx->v2r[ctx->vcount] = j;
	ctx->hdrs[j]->limited = 1;
	ctx->vcount++;
	ctx->vsize += THIS_BODY->length + THIS_BODY->offset - THIS_BODY->hdr_offset;
387 388 389 390
      }
    }
#undef THIS_BODY
  }
391

392
  /* save the list of new messages */
393
  if (oldcount && check != MUTT_REOPENED
394 395
      && ((Sort & SORT_MASK) == SORT_THREADS))
  {
Thomas Roessler's avatar
Thomas Roessler committed
396 397 398
    save_new = (HEADER **) safe_malloc (sizeof (HEADER *) * (ctx->msgcount - oldcount));
    for (j = oldcount; j < ctx->msgcount; j++)
      save_new[j-oldcount] = ctx->hdrs[j];
399
  }
400

401
  /* if the mailbox was reopened, need to rethread from scratch */
402
  mutt_sort_headers (ctx, (check == MUTT_REOPENED));
403

404 405 406
  /* uncollapse threads with new mail */
  if ((Sort & SORT_MASK) == SORT_THREADS)
  {
407
    if (check == MUTT_REOPENED)
408
    {
409
      THREAD *h, *j;
410

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

Thomas Roessler's avatar
Thomas Roessler committed
413
      for (h = ctx->tree; h; h = h->next)
414
      {
415 416
	for (j = h; !j->message; j = j->child)
	  ;
Thomas Roessler's avatar
Thomas Roessler committed
417
	mutt_uncollapse_thread (ctx, j->message);
418
      }
Thomas Roessler's avatar
Thomas Roessler committed
419
      mutt_set_virtual (ctx);
420 421 422
    }
    else if (oldcount)
    {
Thomas Roessler's avatar
Thomas Roessler committed
423
      for (j = 0; j < ctx->msgcount - oldcount; j++)
424 425
      {
	int k;
426

Thomas Roessler's avatar
Thomas Roessler committed
427
	for (k = 0; k < ctx->msgcount; k++)
428
	{
Thomas Roessler's avatar
Thomas Roessler committed
429 430 431
	  HEADER *h = ctx->hdrs[k];
	  if (h == save_new[j] && (!ctx->pattern || h->limited))
	    mutt_uncollapse_thread (ctx, h);
432 433 434
	}
      }
      FREE (&save_new);
Thomas Roessler's avatar
Thomas Roessler committed
435
      mutt_set_virtual (ctx);
436 437
    }
  }
438

439 440 441 442
  menu->current = -1;
  if (oldcount)
  {
    /* restore the current message to the message it was pointing to */
Thomas Roessler's avatar
Thomas Roessler committed
443
    for (j = 0; j < ctx->vcount; j++)
444
    {
Thomas Roessler's avatar
Thomas Roessler committed
445
      if (ctx->hdrs[ctx->v2r[j]]->index == menu->oldcurrent)
446 447 448 449 450 451
      {
	menu->current = j;
	break;
      }
    }
  }
452

453 454
  if (menu->current < 0)
    menu->current = ci_first_message ();
455

456 457
}

458 459 460
static void resort_index (MUTTMENU *menu)
{
  int i;
461
  HEADER *current = CURHDR;
462

463
  menu->current = -1;
464 465
  mutt_sort_headers (Context, 0);
  /* Restore the current message */
466

467 468 469 470 471 472 473 474
  for (i = 0; i < Context->vcount; i++)
  {
    if (Context->hdrs[Context->v2r[i]] == current)
    {
      menu->current = i;
      break;
    }
  }
475

476
  if ((Sort & SORT_MASK) == SORT_THREADS && menu->current < 0)
477
    menu->current = mutt_parent_message (Context, current, 0);
478

479 480
  if (menu->current < 0)
    menu->current = ci_first_message ();
481

482 483 484
  menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
}

485
static const struct mapping_t IndexHelp[] = {
486 487 488 489 490 491 492 493
  { 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 },
494
  { NULL,	 0 }
Thomas Roessler's avatar
Thomas Roessler committed
495 496 497 498 499
};

/* This function handles the message index window as well as commands returned
 * from the pager (MENU_PAGER).
 */
500
int mutt_index_menu (void)
Thomas Roessler's avatar
Thomas Roessler committed
501
{
502
  char buf[LONG_STRING], helpstr[LONG_STRING];
503
  int op = OP_NULL;
Thomas Roessler's avatar
Thomas Roessler committed
504 505 506 507 508 509 510 511 512 513
  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
514
  int close = 0; /* did we OP_QUIT or OP_EXIT out of this menu? */
515
  int attach_msg = option(OPTATTACHMSG);
516

517
  menu = mutt_new_menu (MENU_MAIN);
Thomas Roessler's avatar
Thomas Roessler committed
518 519 520 521
  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);
522 523

  if (!attach_msg)
Thomas Roessler's avatar
Thomas Roessler committed
524
    mutt_buffy_check(1); /* force the buffy check after we enter the folder */
Thomas Roessler's avatar
Thomas Roessler committed
525 526 527 528 529

  FOREVER
  {
    tag = 0; /* clear the tag-prefix */

Thomas Roessler's avatar
Thomas Roessler committed
530 531 532 533
    /* 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
     */
534
    if (option (OPTNEEDRESORT) && Context && Context->msgcount && menu->current >= 0)
Thomas Roessler's avatar
Thomas Roessler committed
535
      resort_index (menu);
536 537 538 539

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

540 541 542 543 544 545
    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
546

Thomas Roessler's avatar
Thomas Roessler committed
547
    if (Context && !attach_msg)
Thomas Roessler's avatar
Thomas Roessler committed
548
    {
549
      int check;
Thomas Roessler's avatar
Thomas Roessler committed
550 551 552 553
      /* 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.)
       */
554

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

557
      if ((check = mx_check_mailbox (Context, &index_hint)) < 0)
Thomas Roessler's avatar
Thomas Roessler committed
558 559 560 561
      {
	if (!Context->path)
	{
	  /* fatal error occurred */
562
	  FREE (&Context);
Thomas Roessler's avatar
Thomas Roessler committed
563 564 565 566 567
	  menu->redraw = REDRAW_FULL;
	}

	set_option (OPTSEARCHINVALID);
      }
568
      else if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED || check == MUTT_FLAGS)
Thomas Roessler's avatar
Thomas Roessler committed
569
      {
570
	update_index (menu, Context, check, oldcount, index_hint);
571

Thomas Roessler's avatar
Thomas Roessler committed
572
	/* notify the user of new mail */
573
	if (check == MUTT_REOPENED)
574
	  mutt_error _("Mailbox was externally modified.  Flags may be wrong.");
575
	else if (check == MUTT_NEW_MAIL)
Thomas Roessler's avatar
Thomas Roessler committed
576
	{
577
	  mutt_message _("New mail in this mailbox.");
Thomas Roessler's avatar
Thomas Roessler committed
578 579
	  if (option (OPTBEEPNEW))
	    beep ();
580
	} else if (check == MUTT_FLAGS)
581 582
	  mutt_message _("Mailbox was externally modified.");

Thomas Roessler's avatar
Thomas Roessler committed
583 584
	/* avoid the message being overwritten by buffy */
	do_buffy_notify = 0;
585

Thomas Roessler's avatar
Thomas Roessler committed
586 587
	menu->redraw = REDRAW_FULL;
	menu->max = Context->vcount;
588

Thomas Roessler's avatar
Thomas Roessler committed
589 590
	set_option (OPTSEARCHINVALID);
      }
591 592
    }

Thomas Roessler's avatar
Thomas Roessler committed
593
    if (!attach_msg)
Thomas Roessler's avatar
Thomas Roessler committed
594
    {
Thomas Roessler's avatar
Thomas Roessler committed
595 596 597 598 599 600
     /* 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)
     {
601 602 603 604 605 606
       if (mutt_buffy_notify())
       {
         menu->redraw |= REDRAW_STATUS;
         if (option (OPTBEEPNEW))
           beep();
       }
Thomas Roessler's avatar
Thomas Roessler committed
607 608 609
     }
     else
       do_buffy_notify = 1;
Thomas Roessler's avatar
Thomas Roessler committed
610 611
    }

612 613
    if (op != -1)
      mutt_curs_set (0);
Thomas Roessler's avatar
Thomas Roessler committed
614 615 616 617 618 619 620 621 622

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

    if (menu->menu == MENU_MAIN)
    {
623 624 625
#ifdef USE_SIDEBAR
      if (menu->redraw & REDRAW_SIDEBAR || SidebarNeedsRedraw)
      {
626
        mutt_sb_set_buffystats (Context);
627 628 629
        menu_redraw_sidebar (menu);
      }
#endif
Thomas Roessler's avatar
Thomas Roessler committed
630
      if (Context && Context->hdrs && !(menu->current >= Context->vcount))
Thomas Roessler's avatar
Thomas Roessler committed
631 632 633 634 635 636 637 638 639 640 641 642 643 644
      {
	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);
      }

645
      if (menu->redraw & REDRAW_STATUS)
Thomas Roessler's avatar
Thomas Roessler committed
646
      {
647
	menu_status_line (buf, sizeof (buf), menu, NONULL (Status));
648
        mutt_window_move (MuttStatusWindow, 0, 0);
Thomas Roessler's avatar
Thomas Roessler committed
649
	SETCOLOR (MT_COLOR_STATUS);
650
	mutt_paddstr (MuttStatusWindow->cols, buf);
651
	NORMAL_COLOR;
Thomas Roessler's avatar
Thomas Roessler committed
652
	menu->redraw &= ~REDRAW_STATUS;
653 654 655 656 657 658 659
	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);
	}
Thomas Roessler's avatar
Thomas Roessler committed
660 661 662
      }

      menu->redraw = 0;
663 664 665 666
      if (menu->current < menu->max)
	menu->oldcurrent = menu->current;
      else
	menu->oldcurrent = -1;
Thomas Roessler's avatar
Thomas Roessler committed
667 668

      if (option (OPTARROWCURSOR))
669
	mutt_window_move (MuttIndexWindow, menu->current - menu->top + menu->offset, 2);
670
      else if (option (OPTBRAILLEFRIENDLY))
671
	mutt_window_move (MuttIndexWindow, menu->current - menu->top + menu->offset, 0);
Thomas Roessler's avatar
Thomas Roessler committed
672
      else
673 674
	mutt_window_move (MuttIndexWindow, menu->current - menu->top + menu->offset,
                          MuttIndexWindow->cols - 1);
Thomas Roessler's avatar
Thomas Roessler committed
675 676 677
      mutt_refresh ();

#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
678
      if (SigWinch)
Thomas Roessler's avatar
Thomas Roessler committed
679 680 681 682
      {
	mutt_flushinp ();
	mutt_resize_screen ();
	menu->redraw = REDRAW_FULL;
683
	menu->menu = MENU_MAIN;
684
	SigWinch = 0;
Thomas Roessler's avatar
Thomas Roessler committed
685
	menu->top = 0; /* so we scroll the right amount */
686 687 688 689 690
	/*
	 * 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
691 692 693 694
	continue;
      }
#endif

695 696 697 698
      op = km_dokey (MENU_MAIN);

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

Thomas Roessler's avatar
Thomas Roessler committed
699 700
      if (op == -1)
	continue; /* either user abort or timeout */
701

702
      mutt_curs_set (1);
703

Thomas Roessler's avatar
Thomas Roessler committed
704 705 706 707 708
      /* special handling for the tag-prefix function */
      if (op == OP_TAG_PREFIX)
      {
	if (!Context)
	{
709
	  mutt_error _("No mailbox is open.");
Thomas Roessler's avatar
Thomas Roessler committed
710 711 712 713 714
	  continue;
	}

	if (!Context->tagged)
	{
715
	  mutt_error _("No tagged messages.");
Thomas Roessler's avatar
Thomas Roessler committed
716 717 718 719 720
	  continue;
	}
	tag = 1;

	/* give visual indication that the next command is a tag- command */
721 722
	mutt_window_mvaddstr (MuttMessageWindow, 0, 0, "tag-");
	mutt_window_clrtoeol (MuttMessageWindow);
Thomas Roessler's avatar
Thomas Roessler committed
723 724 725 726 727

	/* get the real command */
	if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX)
	{
	  /* abort tag sequence */
728
          mutt_window_clearline (MuttMessageWindow, 0);
Thomas Roessler's avatar
Thomas Roessler committed
729 730 731 732 733 734
	  continue;
	}
      }
      else if (option (OPTAUTOTAG) && Context && Context->tagged)
	tag = 1;

735 736 737 738 739 740 741 742 743 744
      if (op == OP_TAG_PREFIX_COND)
      {
	if (!Context)
	{
	  mutt_error _("No mailbox is open.");
	  continue;
	}

	if (!Context->tagged)
	{
745
	  mutt_flush_macro_to_endcond ();
746 747 748 749 750 751
	  mutt_message  _("Nothing to do.");
	  continue;
	}
	tag = 1;

	/* give visual indication that the next command is a tag- command */
752 753
	mutt_window_mvaddstr (MuttMessageWindow, 0, 0, "tag-");
	mutt_window_clrtoeol (MuttMessageWindow);
754 755 756 757 758

	/* get the real command */
	if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX)
	{
	  /* abort tag sequence */
759
	  mutt_window_clearline (MuttMessageWindow, 0);
760 761 762 763
	  continue;
	}
      }

Thomas Roessler's avatar
Thomas Roessler committed
764 765 766
      mutt_clear_error ();
    }
    else
767 768 769 770 771
    {
      if (menu->current < menu->max)
	menu->oldcurrent = menu->current;
      else
	menu->oldcurrent = -1;
772

Thomas Roessler's avatar
Thomas Roessler committed
773
      mutt_curs_set (1);	/* fallback from the pager */
774
    }
Thomas Roessler's avatar
Thomas Roessler committed
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828

    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;
829
        CHECK_VISIBLE;
830
        if (isdigit (LastKey)) mutt_unget_event (LastKey, 0);
Thomas Roessler's avatar
Thomas Roessler committed
831
	buf[0] = 0;
832 833
	if (mutt_get_field (_("Jump to message: "), buf, sizeof (buf), 0) != 0
	    || !buf[0])
Thomas Roessler's avatar
Thomas Roessler committed
834 835
	  break;

836
	if (mutt_atoi (buf, &i) < 0)
Thomas Roessler's avatar
Thomas Roessler committed
837
	{
838
	  mutt_error _("Argument must be a message number.");
Thomas Roessler's avatar
Thomas Roessler committed
839 840 841 842 843 844 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
	  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
	    menu->redraw = REDRAW_MOTION;
	  }
	  else
870
	    mutt_error _("That message is not visible.");
Thomas Roessler's avatar
Thomas Roessler committed
871 872
	}
	else
873
	  mutt_error _("Invalid message number.");
Thomas Roessler's avatar
Thomas Roessler committed
874 875 876 877 878 879 880 881 882 883

	break;

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

      case OP_MAIN_DELETE_PATTERN:

	CHECK_MSGCOUNT;
884
        CHECK_VISIBLE;
Thomas Roessler's avatar
Thomas Roessler committed
885
	CHECK_READONLY;
886
        /* L10N: CHECK_ACL */
887
	CHECK_ACL(MUTT_ACL_DELETE, _("Cannot delete message(s)"));
888

889
	CHECK_ATTACH;
890
	mutt_pattern_func (MUTT_DELETE, _("Delete messages matching: "));
Thomas Roessler's avatar
Thomas Roessler committed
891 892 893 894 895 896
	menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
	break;

#ifdef USE_POP
      case OP_MAIN_FETCH_MAIL:

897
	CHECK_ATTACH;
898
	pop_fetch_mail ();
Thomas Roessler's avatar
Thomas Roessler committed
899 900 901 902 903 904 905 906 907 908 909
	menu->redraw = REDRAW_FULL;
	break;
#endif /* USE_POP */

      case OP_HELP:

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

      case OP_MAIN_SHOW_LIMIT:
910
	CHECK_IN_MAILBOX;
Thomas Roessler's avatar
Thomas Roessler committed
911
	if (!Context->pattern)
912
	   mutt_message _("No limit pattern is in effect.");
Thomas Roessler's avatar
Thomas Roessler committed
913 914 915
	else
	{
	   char buf[STRING];
916
	   /* L10N: ask for a limit to apply */
917
	   snprintf (buf, sizeof(buf), _("Limit: %s"),Context->pattern);
Thomas Roessler's avatar
Thomas Roessler committed
918 919 920 921 922 923
           mutt_message ("%s", buf);
	}
        break;

      case OP_MAIN_LIMIT:

924
	CHECK_IN_MAILBOX;
925
	menu->oldcurrent = (Context->vcount && menu->current >= 0 && menu->current < Context->vcount) ?
926
		CURHDR->index : -1;
927
	if (mutt_pattern_func (MUTT_LIMIT, _("Limit to messages matching: ")) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
	{
	  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;
	  menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
944
	  if (Context->msgcount && (Sort & SORT_MASK) == SORT_THREADS)
945
	    mutt_draw_tree (Context);
946
	  menu->redraw = REDRAW_FULL;
Thomas Roessler's avatar
Thomas Roessler committed
947
	}
948 949
        if (Context->pattern)
	  mutt_message _("To view all messages, limit to \"all\".");
950
	break;
Thomas Roessler's avatar
Thomas Roessler committed
951 952 953

      case OP_QUIT:

Thomas Roessler's avatar
Thomas Roessler committed
954 955 956 957 958 959 960
	close = op;
	if (attach_msg)
	{
	 done = 1;
	 break;
	}

961
	if (query_quadoption (OPT_QUIT, _("Quit Mutt?")) == MUTT_YES)
962 963
	{
	  int check;
964

965
	  oldcount = Context ? Context->msgcount : 0;
966 967

	  if (!Context || (check = mx_close_mailbox (Context, &index_hint)) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
968 969
	    done = 1;
	  else
970
	  {
971
	    if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED)
972 973
	      update_index (menu, Context, check, oldcount, index_hint);

Thomas Roessler's avatar
Thomas Roessler committed
974
	    menu->redraw = REDRAW_FULL; /* new mail arrived? */
975 976
	    set_option (OPTSEARCHINVALID);
	  }
Thomas Roessler's avatar
Thomas Roessler committed
977 978 979 980 981 982 983 984 985 986 987 988 989 990 991
	}
	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;
992
        CHECK_VISIBLE;
993
	if ((menu->current = mutt_search_command (menu->current, op)) == -1)
Thomas Roessler's avatar
Thomas Roessler committed
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
	  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)
	  {
1006
	    resort_index (menu);
Thomas Roessler's avatar
Thomas Roessler committed
1007 1008
	    set_option (OPTSEARCHINVALID);
	  }
1009 1010 1011 1012 1013
	  if (menu->menu == MENU_PAGER)
	  {
	    op = OP_DISPLAY_MESSAGE;
	    continue;
	  }
1014
	  menu->redraw |= REDRAW_STATUS;
Thomas Roessler's avatar
Thomas Roessler committed
1015 1016 1017 1018 1019 1020
	}
	break;

      case OP_TAG:

	CHECK_MSGCOUNT;
1021
        CHECK_VISIBLE;
Thomas Roessler's avatar
Thomas Roessler committed
1022 1023 1024
	if (tag && !option (OPTAUTOTAG))
	{
	  for (j = 0; j < Context->vcount; j++)
1025
	    mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], MUTT_TAG, 0);
Thomas Roessler's avatar
Thomas Roessler committed
1026 1027 1028 1029
	  menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
	}
	else
	{
1030
	  mutt_set_flag (Context, CURHDR, MUTT_TAG, !CURHDR->tagged);
1031 1032 1033 1034 1035

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

Thomas Roessler's avatar
Thomas Roessler committed
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
	  menu->redraw = REDRAW_STATUS;
	  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;
1050
        CHECK_VISIBLE;
1051
	mutt_pattern_func (MUTT_TAG, _("Tag messages matching: "));
Thomas Roessler's avatar
Thomas Roessler committed
1052 1053 1054 1055 1056 1057
	menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
	break;

      case OP_MAIN_UNDELETE_PATTERN:

	CHECK_MSGCOUNT;
1058
        CHECK_VISIBLE;
Thomas Roessler's avatar
Thomas Roessler committed
1059
	CHECK_READONLY;
1060
        /* L10N: CHECK_ACL */
1061
	CHECK_ACL(MUTT_ACL_DELETE, _("Cannot undelete message(s)"));
1062

1063
	if (mutt_pattern_func (MUTT_UNDELETE, _("Undelete messages matching: ")) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
1064 1065 1066 1067 1068 1069
	  menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
	break;

      case OP_MAIN_UNTAG_PATTERN:

	CHECK_MSGCOUNT;
1070
        CHECK_VISIBLE;
1071
	if (mutt_pattern_func (MUTT_UNTAG, _("Untag messages matching: ")) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
1072 1073 1074 1075 1076 1077 1078
	  menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
	break;

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

1079 1080
#ifdef USE_IMAP
      case OP_MAIN_IMAP_FETCH:
1081
	if (Context && Context->magic == MUTT_IMAP)
1082
	  imap_check_mailbox (Context, &index_hint, 1);
1083
        break;
1084 1085

      case OP_MAIN_IMAP_LOGOUT_ALL:
1086
	if (Context && Context->magic == MUTT_IMAP)
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
	{
	  if (mx_close_mailbox (Context, &index_hint) != 0)
	  {
	    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;
1101
#endif
1102

1103 1104
      case OP_MAIN_SYNC_FOLDER:

1105 1106 1107
	if (Context && !Context->msgcount)
	  break;

1108 1109 1110 1111 1112
	CHECK_MSGCOUNT;
	CHECK_READONLY;
	{
	  int oldvcount = Context->vcount;
	  int oldcount  = Context->msgcount;
1113 1114 1115
	  int check, newidx;
	  HEADER *newhdr = NULL;

1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
	  /* 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]];
	  }
1129 1130 1131

	  if ((check = mx_sync_mailbox (Context, &index_hint)) == 0)
	  {
1132 1133 1134 1135 1136 1137 1138 1139 1140
	    if (newhdr && Context->vcount != oldvcount)
	      for (j = 0; j < Context->vcount; j++)
	      {
		if (Context->hdrs[Context->v2r[j]] == newhdr)
		{
		  menu->current = j;
		  break;
		}
	      }
1141 1142
	    set_option (OPTSEARCHINVALID);
	  }
1143
	  else if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED)
1144 1145
	    update_index (menu, Context, check, oldcount, index_hint);

1146
	  /*
1147 1148 1149 1150 1151 1152 1153 1154 1155
	   * 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)
1156
	  FREE (&Context);
1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167

	/* 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;

1168 1169 1170
#ifdef USE_SIDEBAR
      case OP_SIDEBAR_OPEN:
#endif
Thomas Roessler's avatar
Thomas Roessler committed
1171
      case OP_MAIN_CHANGE_FOLDER:
1172
      case OP_MAIN_NEXT_UNREAD_MAILBOX:
1173

Thomas Roessler's avatar
Thomas Roessler committed
1174
	if (attach_msg)
Thomas Roessler's avatar
Thomas Roessler committed
1175 1176 1177 1178 1179 1180
	  op = OP_MAIN_CHANGE_FOLDER_READONLY;

	/* fallback to the readonly case */

      case OP_MAIN_CHANGE_FOLDER_READONLY:

Thomas Roessler's avatar
Thomas Roessler committed
1181
        if ((op == OP_MAIN_CHANGE_FOLDER_READONLY) || option (OPTREADONLY))
1182
          cp = _("Open mailbox in read-only mode");
Thomas Roessler's avatar
Thomas Roessler committed
1183 1184
        else
          cp = _("Open mailbox");
Thomas Roessler's avatar
Thomas Roessler committed
1185 1186

	buf[0] = '\0';
1187
	if ((op == OP_MAIN_NEXT_UNREAD_MAILBOX) && Context && Context->path)
Thomas Roessler's avatar
Thomas Roessler committed
1188
	{
N.J. Mann's avatar
N.J. Mann committed
1189
	  strfcpy (buf, Context->path, sizeof (buf));
1190
	  mutt_pretty_mailbox (buf, sizeof (buf));
N.J. Mann's avatar
N.J. Mann committed
1191 1192 1193
	  mutt_buffy (buf, sizeof (buf));
	  if (!buf[0])
	  {
1194
	    mutt_error _("No mailboxes have new mail");
N.J. Mann's avatar
N.J. Mann committed
1195 1196 1197
	    break;
	  }
	}
1198 1199 1200
#ifdef USE_SIDEBAR
        else if (op == OP_SIDEBAR_OPEN)
        {
1201
          const char *path = mutt_sb_get_highlight();
1202 1203 1204 1205 1206
          if (!path || !*path)
            break;
          strncpy (buf, path, sizeof (buf));
        }
#endif
Brendan Cully's avatar
Brendan Cully committed
1207
	else
N.J. Mann's avatar
N.J. Mann committed
1208 1209 1210
	{
	  mutt_buffy (buf, sizeof (buf));

1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
          if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1)
          {
            if (menu->menu == MENU_PAGER)
            {
              op = OP_DISPLAY_MESSAGE;
              continue;
            }
            else
              break;
          }
N.J. Mann's avatar
N.J. Mann committed
1221 1222
	  if (!buf[0])
	  {
1223
            mutt_window_clearline (MuttMessageWindow, 0);
N.J. Mann's avatar
N.J. Mann committed
1224 1225
	    break;
	  }
Thomas Roessler's avatar
Thomas Roessler committed
1226 1227 1228 1229 1230
	}

	mutt_expand_path (buf, sizeof (buf));
	if (mx_get_magic (buf) <= 0)
	{
1231
	  mutt_error (_("%s is not a mailbox."), buf);
Thomas Roessler's avatar
Thomas Roessler committed
1232 1233
	  break;
	}
1234
	mutt_str_replace (&CurrentFolder, buf);
Thomas Roessler's avatar
Thomas Roessler committed
1235

1236 1237 1238 1239
	/* keepalive failure in mutt_enter_fname may kill connection. #3028 */
	if (Context && !Context->path)
	  FREE (&Context);

Thomas Roessler's avatar
Thomas Roessler committed
1240 1241
        if (Context)
        {
1242 1243
	  int check;

1244 1245 1246 1247 1248
#ifdef USE_COMPRESSED
	  if (Context->compress_info && Context->realpath)
	    mutt_str_replace (&LastFolder, Context->realpath);
	  else
#endif
1249
	  mutt_str_replace (&LastFolder, Context->path);
1250
	  oldcount = Context ? Context->msgcount : 0;
Thomas Roessler's avatar
Thomas Roessler committed
1251

1252
	  if ((check = mx_close_mailbox (Context, &index_hint)) != 0)
Thomas Roessler's avatar
Thomas Roessler committed
1253
	  {
1254
	    if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED)
1255
	      update_index (menu, Context, check, oldcount, index_hint);
1256

1257
	    set_option (OPTSEARCHINVALID);
Thomas Roessler's avatar
Thomas Roessler committed
1258 1259 1260
	    menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
	    break;
	  }
1261
	  FREE (&Context);
Thomas Roessler's avatar
Thomas Roessler committed
1262 1263
	}

1264
        mutt_sleep (0);
1265

1266 1267 1268 1269 1270 1271
	/* Set CurrentMenu to MENU_MAIN before executing any folder
	 * hooks so that all the index menu functions are available to
	 * the exec command.
	 */

	CurrentMenu = MENU_MAIN;
Thomas Roessler's avatar
Thomas Roessler committed
1272 1273
	mutt_folder_hook (buf);

1274
	if ((Context = mx_open_mailbox (buf,
Thomas Roessler's avatar
Thomas Roessler committed
1275
					(option (OPTREADONLY) || op == OP_MAIN_CHANGE_FOLDER_READONLY) ?
1276
					MUTT_READONLY : 0, NULL)) != NULL)
Thomas Roessler's avatar
Thomas Roessler committed
1277 1278 1279 1280 1281 1282
	{
	  menu->current = ci_first_message ();
	}
	else
	  menu->current = 0;

1283 1284 1285 1286
#ifdef USE_SIDEBAR
        mutt_sb_set_open_buffy ();
#endif

Thomas Roessler's avatar
Thomas Roessler committed
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297
	mutt_clear_error ();
	mutt_buffy_check(1); /* force the buffy check after we have changed
			      the folder */
	menu->redraw = REDRAW_FULL;
	set_option (OPTSEARCHINVALID);
	break;

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

	CHECK_MSGCOUNT;
1298
        CHECK_VISIBLE;
Thomas Roessler's avatar
Thomas Roessler committed
1299 1300 1301 1302 1303 1304 1305 1306
	/*
	 * 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);
1307

1308 1309 1310 1311
	if ((Sort & SORT_MASK) == SORT_THREADS && CURHDR->collapsed)
	{
	  mutt_uncollapse_thread (Context, CURHDR);
	  mutt_set_virtual (Context);
1312 1313
	  if (option (OPTUNCOLLAPSEJUMP))
	    menu->current = mutt_thread_next_unread (Context, CURHDR);
1314
	}
1315 1316

	if (option (OPTPGPAUTODEC) && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED)))
1317
	  mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1318
	if ((op = mutt_display_message (CURHDR)) == -1)
Thomas Roessler's avatar
Thomas Roessler committed
1319 1320 1321 1322 1323
	{
	  unset_option (OPTNEEDRESORT);
	  break;
	}

1324
	menu->menu = MENU_PAGER;
1325
 	menu->oldcurrent = menu->current;
Thomas Roessler's avatar
Thomas Roessler committed
1326 1327 1328 1329
	continue;

      case OP_EXIT:

Thomas Roessler's avatar
Thomas Roessler committed
1330 1331 1332 1333 1334 1335 1336
	close = op;
	if (menu->menu == MENU_MAIN && attach_msg)
	{
	 done = 1;
	 break;
	}

Thomas Roessler's avatar
Thomas Roessler committed
1337
	if ((menu->menu == MENU_MAIN)
1338
	    && (query_quadoption (OPT_QUIT,
1339
				  _("Exit Mutt without saving?")) == MUTT_YES))
1340
	{
1341 1342 1343
	  if (Context)
	  {
	    mx_fastclose_mailbox (Context);
1344
	    FREE (&Context);
1345
	  }
Thomas Roessler's avatar
Thomas Roessler committed
1346
	  done = 1;
1347
	}
Thomas Roessler's avatar
Thomas Roessler committed
1348 1349
	break;

1350 1351 1352 1353 1354 1355 1356 1357
      case OP_MAIN_BREAK_THREAD:

	CHECK_MSGCOUNT;
        CHECK_VISIBLE;
	CHECK_READONLY;

        if ((Sort & SORT_MASK) != SORT_THREADS)
	  mutt_error _("Threading is not enabled.");
1358
	else if (CURHDR->env->in_reply_to || CURHDR->env->references)
1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378
	{
	  {
	    HEADER *oldcur = CURHDR;

	    mutt_break_thread (CURHDR);
	    mutt_sort_headers (Context, 1);
	    menu->current = oldcur->virtual;
	  }

	  Context->changed = 1;
	  mutt_message _("Thread broken");

	  if (menu->menu == MENU_PAGER)
	  {
	    op = OP_DISPLAY_MESSAGE;
	    continue;
	  }
	  else
	    menu->redraw |= REDRAW_INDEX;
	}
1379 1380
	else
	  mutt_error _("Thread cannot be broken, message is not part of a thread");
1381

1382
	break;
1383 1384 1385 1386 1387 1388

      case OP_MAIN_LINK_THREADS:

	CHECK_MSGCOUNT;
        CHECK_VISIBLE;
	CHECK_READONLY;
1389
        /* L10N: CHECK_ACL */
1390
	CHECK_ACL(MUTT_ACL_DELETE, _("Cannot link threads"));
1391 1392 1393 1394 1395 1396 1397

        if ((Sort & SORT_MASK) != SORT_THREADS)
	  mutt_error _("Threading is not enabled.");
	else if (!CURHDR->env->message_id)
	  mutt_error _("No Message-ID: header available to link thread");
	else if (!tag && (!Context->last_tag || !Context->last_tag->tagged))
	  mutt_error _("First, please tag a message to be linked here");
1398
	else
1399 1400 1401 1402 1403 1404 1405 1406
	{
	  HEADER *oldcur = CURHDR;

	  if (mutt_link_threads (CURHDR, tag ? NULL : Context->last_tag,
				 Context))
	  {
	    mutt_sort_headers (Context, 1);
	    menu->current = oldcur->virtual;
1407

1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424
	    Context->changed = 1;
	    mutt_message _("Threads linked");
	  }
	  else
	    mutt_error _("No thread linked");
	}

	if (menu->menu == MENU_PAGER)
	{
	  op = OP_DISPLAY_MESSAGE;
	  continue;
	}
	else
	  menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;

	break;

1425 1426 1427
      case OP_EDIT_TYPE:

	CHECK_MSGCOUNT;
1428
        CHECK_VISIBLE;
1429
	CHECK_ATTACH;