Andreas Krennmair:
[apps/madmutt.git] / curs_main.c
1 /*
2  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
3  * 
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  * 
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  * 
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
17  */
18
19 #if HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include "mutt.h"
24 #include "mutt_curses.h"
25 #include "mx.h"
26 #include "mutt_menu.h"
27 #include "mailbox.h"
28 #include "mapping.h"
29 #include "sort.h"
30 #include "buffy.h"
31 #include "mx.h"
32 #include "sidebar.h"
33
34 #ifdef USE_POP
35 #include "pop.h"
36 #endif
37
38 #ifdef USE_IMAP
39 #include "imap_private.h"
40 #endif
41
42 #include "mutt_crypt.h"
43
44 #ifdef USE_NNTP
45 #include "nntp.h"
46 #endif
47
48
49 #include <ctype.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <sys/wait.h>
53 #include <string.h>
54 #include <sys/stat.h>
55 #include <errno.h>
56
57 static const char *No_mailbox_is_open = N_("No mailbox is open.");
58 static const char *There_are_no_messages = N_("There are no messages.");
59 static const char *Mailbox_is_read_only = N_("Mailbox is read-only.");
60 static const char *Function_not_permitted_in_attach_message_mode =
61 N_("Function not permitted in attach-message mode.");
62 static const char *No_visible = N_("No visible messages.");
63
64 #define CHECK_MSGCOUNT if (!Context) \
65         { \
66                   mutt_flushinp (); \
67                 mutt_error(_(No_mailbox_is_open)); \
68                 break; \
69         } \
70         else if (!Context->msgcount) \
71         { \
72                   mutt_flushinp (); \
73                 mutt_error(_(There_are_no_messages)); \
74                 break; \
75         }
76
77 #define CHECK_VISIBLE if (Context && menu->current >= Context->vcount) \
78           {\
79                   mutt_flushinp (); \
80                   mutt_error(_(No_visible)); \
81                   break; \
82         }
83
84
85 #define CHECK_READONLY if (Context->readonly) \
86                         { \
87                                   mutt_flushinp (); \
88                                 mutt_error(_(Mailbox_is_read_only)); \
89                                 break; \
90                         }
91
92 #ifdef USE_IMAP
93 /* the error message returned here could be better. */
94 #define CHECK_IMAP_ACL(aclbit) if (Context->magic == M_IMAP) \
95                 if (mutt_bit_isset (((IMAP_DATA *)Context->data)->capabilities, ACL) \
96                 && !mutt_bit_isset(((IMAP_DATA *)Context->data)->rights,aclbit)){ \
97                         mutt_flushinp(); \
98                         mutt_error ("Operation not permitted by the IMAP ACL for this mailbox"); \
99                         break; \
100                 }
101 #endif
102
103 #define CHECK_ATTACH if(option(OPTATTACHMSG)) \
104                      {\
105                         mutt_flushinp (); \
106                         mutt_error(_(Function_not_permitted_in_attach_message_mode)); \
107                         break; \
108                      }
109
110 #define CURHDR Context->hdrs[Context->v2r[menu->current]]
111 #define OLDHDR Context->hdrs[Context->v2r[menu->oldcurrent]]
112 #define UNREAD(h) mutt_thread_contains_unread (Context, h)
113
114 extern const char *ReleaseDate;
115 extern size_t UngetCount;
116
117 static void set_xterm_title_bar (char *title)
118 {
119   fputs ("\033]2;", stdout);
120   fputs (title, stdout);
121   fputs ("\007", stdout);
122   fflush (stdout);
123 }
124
125 static void set_xterm_icon_name (char *name)
126 {
127   fputs ("\033]1;", stdout);
128   fputs (name, stdout);
129   fputs ("\007", stdout);
130   fflush (stdout);
131 }
132
133 void index_make_entry (char *s, size_t l, MUTTMENU * menu, int num)
134 {
135   format_flag flag =
136     M_FORMAT_MAKEPRINT | M_FORMAT_ARROWCURSOR | M_FORMAT_INDEX;
137   int edgemsgno, reverse = Sort & SORT_REVERSE;
138   HEADER *h = Context->hdrs[Context->v2r[num]];
139   THREAD *tmp;
140
141   if ((Sort & SORT_MASK) == SORT_THREADS && h->tree) {
142     flag |= M_FORMAT_TREE;      /* display the thread tree */
143     if (h->display_subject)
144       flag |= M_FORMAT_FORCESUBJ;
145     else {
146       if (reverse) {
147         if (menu->top + menu->pagelen > menu->max)
148           edgemsgno = Context->v2r[menu->max - 1];
149         else
150           edgemsgno = Context->v2r[menu->top + menu->pagelen - 1];
151       }
152       else
153         edgemsgno = Context->v2r[menu->top];
154
155       for (tmp = h->thread->parent; tmp; tmp = tmp->parent) {
156         if (!tmp->message)
157           continue;
158
159         /* if no ancestor is visible on current screen, provisionally force
160          * subject... */
161         if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno <
162             edgemsgno) {
163           flag |= M_FORMAT_FORCESUBJ;
164           break;
165         }
166         else if (tmp->message->virtual >= 0)
167           break;
168       }
169       if (flag & M_FORMAT_FORCESUBJ) {
170         for (tmp = h->thread->prev; tmp; tmp = tmp->prev) {
171           if (!tmp->message)
172             continue;
173
174           /* ...but if a previous sibling is available, don't force it */
175           if (reverse ? tmp->message->msgno >
176               edgemsgno : tmp->message->msgno < edgemsgno)
177             break;
178           else if (tmp->message->virtual >= 0) {
179             flag &= ~M_FORMAT_FORCESUBJ;
180             break;
181           }
182         }
183       }
184     }
185   }
186
187   _mutt_make_string (s, l, NONULL (HdrFmt), Context, h, flag);
188 }
189
190 int index_color (int index_no)
191 {
192   HEADER *h = Context->hdrs[Context->v2r[index_no]];
193
194   if (h && h->pair)
195     return h->pair;
196
197   mutt_set_header_color (Context, h);
198   return h->pair;
199 }
200
201 static int ci_next_undeleted (int msgno)
202 {
203   int i;
204
205   for (i = msgno + 1; i < Context->vcount; i++)
206     if (!Context->hdrs[Context->v2r[i]]->deleted)
207       return (i);
208   return (-1);
209 }
210
211 static int ci_previous_undeleted (int msgno)
212 {
213   int i;
214
215   for (i = msgno - 1; i >= 0; i--)
216     if (!Context->hdrs[Context->v2r[i]]->deleted)
217       return (i);
218   return (-1);
219 }
220
221 /* Return the index of the first new message, or failing that, the first
222  * unread message.
223  */
224 static int ci_first_message (void)
225 {
226   int old = -1, i;
227
228   if (Context && Context->msgcount) {
229     for (i = 0; i < Context->vcount; i++) {
230       if (!Context->hdrs[Context->v2r[i]]->read &&
231           !Context->hdrs[Context->v2r[i]]->deleted) {
232         if (!Context->hdrs[Context->v2r[i]]->old)
233           return (i);
234         else if (old == -1)
235           old = i;
236       }
237     }
238     if (old != -1)
239       return (old);
240
241     /* If Sort is reverse and not threaded, the latest message is first.
242      * If Sort is threaded, the latest message is first iff exactly one
243      * of Sort and SortAux are reverse.
244      */
245     if (((Sort & SORT_REVERSE) && (Sort & SORT_MASK) != SORT_THREADS) ||
246         ((Sort & SORT_MASK) == SORT_THREADS &&
247          ((Sort ^ SortAux) & SORT_REVERSE)))
248       return 0;
249     else
250       return (Context->vcount ? Context->vcount - 1 : 0);
251   }
252   return 0;
253 }
254
255 /* This should be in mx.c, but it only gets used here. */
256 static int mx_toggle_write (CONTEXT * ctx)
257 {
258   if (!ctx)
259     return -1;
260
261   if (ctx->readonly) {
262     mutt_error (_("Cannot toggle write on a readonly mailbox!"));
263
264     return -1;
265   }
266
267   if (ctx->dontwrite) {
268     ctx->dontwrite = 0;
269     mutt_message (_("Changes to folder will be written on folder exit."));
270   }
271   else {
272     ctx->dontwrite = 1;
273     mutt_message (_("Changes to folder will not be written."));
274   }
275
276   return 0;
277 }
278
279 static void update_index (MUTTMENU * menu, CONTEXT * ctx, int check,
280                           int oldcount, int index_hint)
281 {
282   /* store pointers to the newly added messages */
283   HEADER **save_new = NULL;
284   int j;
285
286   /* take note of the current message */
287   if (oldcount) {
288     if (menu->current < Context->vcount)
289       menu->oldcurrent = index_hint;
290     else
291       oldcount = 0;             /* invalid message number! */
292   }
293
294   /* We are in a limited view. Check if the new message(s) satisfy
295    * the limit criteria. If they do, set their virtual msgno so that
296    * they will be visible in the limited view */
297   if (Context->pattern) {
298 #define THIS_BODY Context->hdrs[j]->content
299     if (oldcount || check == M_REOPENED) {
300       for (j = (check == M_REOPENED) ? 0 : oldcount; j < Context->msgcount;
301            j++) {
302         if (mutt_pattern_exec
303             (Context->limit_pattern, M_MATCH_FULL_ADDRESS, Context,
304              Context->hdrs[j])) {
305           Context->hdrs[j]->virtual = Context->vcount;
306           Context->v2r[Context->vcount] = j;
307           Context->hdrs[j]->limited = 1;
308           Context->vcount++;
309           Context->vsize +=
310             THIS_BODY->length + THIS_BODY->offset - THIS_BODY->hdr_offset;
311         }
312       }
313     }
314 #undef THIS_BODY
315   }
316
317   /* save the list of new messages */
318   if (oldcount && check != M_REOPENED && ((Sort & SORT_MASK) == SORT_THREADS)) {
319     save_new =
320       (HEADER **) safe_malloc (sizeof (HEADER *) *
321                                (Context->msgcount - oldcount));
322     for (j = oldcount; j < Context->msgcount; j++)
323       save_new[j - oldcount] = Context->hdrs[j];
324   }
325
326   /* if the mailbox was reopened, need to rethread from scratch */
327   mutt_sort_headers (Context, (check == M_REOPENED));
328
329   /* uncollapse threads with new mail */
330   if ((Sort & SORT_MASK) == SORT_THREADS) {
331     if (check == M_REOPENED) {
332       THREAD *h, *j;
333
334       Context->collapsed = 0;
335
336       for (h = Context->tree; h; h = h->next) {
337         for (j = h; !j->message; j = j->child);
338         mutt_uncollapse_thread (Context, j->message);
339       }
340       mutt_set_virtual (Context);
341     }
342     else if (oldcount) {
343       for (j = 0; j < Context->msgcount - oldcount; j++) {
344         int k;
345
346         for (k = 0; k < Context->msgcount; k++) {
347           HEADER *h = Context->hdrs[k];
348
349           if (h == save_new[j] && (!Context->pattern || h->limited))
350             mutt_uncollapse_thread (Context, h);
351         }
352       }
353       FREE (&save_new);
354       mutt_set_virtual (Context);
355     }
356   }
357
358   menu->current = -1;
359   if (oldcount) {
360     /* restore the current message to the message it was pointing to */
361     for (j = 0; j < Context->vcount; j++) {
362       if (Context->hdrs[Context->v2r[j]]->index == menu->oldcurrent) {
363         menu->current = j;
364         break;
365       }
366     }
367   }
368
369   if (menu->current < 0)
370     menu->current = ci_first_message ();
371
372 }
373
374 static void resort_index (MUTTMENU * menu)
375 {
376   int i;
377   HEADER *current = CURHDR;
378
379   menu->current = -1;
380   mutt_sort_headers (Context, 0);
381   /* Restore the current message */
382
383   for (i = 0; i < Context->vcount; i++) {
384     if (Context->hdrs[Context->v2r[i]] == current) {
385       menu->current = i;
386       break;
387     }
388   }
389
390   if ((Sort & SORT_MASK) == SORT_THREADS && menu->current < 0)
391     menu->current = mutt_parent_message (Context, current);
392
393   if (menu->current < 0)
394     menu->current = ci_first_message ();
395
396   menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
397 }
398
399 struct mapping_t IndexHelp[] = {
400   {N_("Quit"), OP_QUIT},
401   {N_("Del"), OP_DELETE},
402   {N_("Undel"), OP_UNDELETE},
403   {N_("Save"), OP_SAVE},
404   {N_("Mail"), OP_MAIL},
405   {N_("Reply"), OP_REPLY},
406   {N_("Group"), OP_GROUP_REPLY},
407   {N_("Help"), OP_HELP},
408   {NULL}
409 };
410
411 #ifdef USE_NNTP
412 struct mapping_t IndexNewsHelp[] = {
413   {N_("Quit"), OP_QUIT},
414   {N_("Del"), OP_DELETE},
415   {N_("Undel"), OP_UNDELETE},
416   {N_("Save"), OP_SAVE},
417   {N_("Post"), OP_POST},
418   {N_("Followup"), OP_FOLLOWUP},
419   {N_("Catchup"), OP_CATCHUP},
420   {N_("Help"), OP_HELP},
421   {NULL}
422 };
423 #endif
424
425 /* This function handles the message index window as well as commands returned
426  * from the pager (MENU_PAGER).
427  */
428 int mutt_index_menu (void)
429 {
430   char buf[LONG_STRING], helpstr[SHORT_STRING];
431   int flags;
432   int op = OP_NULL;
433   int done = 0;                 /* controls when to exit the "event" loop */
434   int i = 0, j;
435   int tag = 0;                  /* has the tag-prefix command been pressed? */
436   int newcount = -1;
437   int oldcount = -1;
438   int rc = -1;
439   MUTTMENU *menu;
440   char *cp;                     /* temporary variable. */
441   int index_hint;               /* used to restore cursor position */
442   int do_buffy_notify = 1;
443   int close = 0;                /* did we OP_QUIT or OP_EXIT out of this menu? */
444   int attach_msg = option (OPTATTACHMSG);
445
446   menu = mutt_new_menu ();
447   menu->menu = MENU_MAIN;
448   menu->offset = 1;
449   menu->pagelen = LINES - 3;
450   menu->make_entry = index_make_entry;
451   menu->color = index_color;
452   menu->current = ci_first_message ();
453   menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN,
454 #ifdef USE_NNTP
455                                   (Context
456                                    && (Context->magic ==
457                                        M_NNTP)) ? IndexNewsHelp :
458 #endif
459                                   IndexHelp);
460
461   if (!attach_msg) {
462     mutt_buffy_check (1);       /* force the buffy check after we enter the folder */
463     /* record folder we open to place sidebar indicator properly */
464     if (Context && Context->path)
465       set_curbuffy (Context->path);
466   }
467
468   FOREVER {
469     tag = 0;                    /* clear the tag-prefix */
470
471     menu->max = Context ? Context->vcount : 0;
472     oldcount = Context ? Context->msgcount : 0;
473
474     /* check if we need to resort the index because just about
475      * any 'op' below could do mutt_enter_command(), either here or
476      * from any new menu launched, and change $sort/$sort_aux
477      */
478     if (option (OPTNEEDRESORT) && Context && Context->msgcount)
479       resort_index (menu);
480
481     if (option (OPTREDRAWTREE) && Context && Context->msgcount
482         && (Sort & SORT_MASK) == SORT_THREADS) {
483       mutt_draw_tree (Context);
484       menu->redraw |= REDRAW_STATUS;
485       unset_option (OPTREDRAWTREE);
486     }
487
488     if (Context && !attach_msg) {
489       int check;
490
491       /* check for new mail in the mailbox.  If nonzero, then something has
492        * changed about the file (either we got new mail or the file was
493        * modified underneath us.)
494        */
495
496 #ifdef USE_IMAP
497       imap_allow_reopen (Context);
498 #endif
499
500       index_hint = (Context->vcount && menu->current >= 0
501                     && menu->current < Context->vcount) ? CURHDR->index : 0;
502
503       if ((check = mx_check_mailbox (Context, &index_hint, 0)) < 0) {
504         if (!Context->path) {
505           /* fatal error occurred */
506           FREE (&Context);
507           menu->redraw = REDRAW_FULL;
508         }
509         set_option (OPTSEARCHINVALID);
510       }
511       else if (check == M_NEW_MAIL || check == M_REOPENED || check == M_FLAGS) {
512         update_index (menu, Context, check, oldcount, index_hint);
513
514         /* notify the user of new mail */
515         if (check == M_REOPENED)
516           mutt_error (_
517                       ("Mailbox was externally modified.  Flags may be wrong."));
518         else if (check == M_NEW_MAIL) {
519           /* on new mail: redraw sidebar */
520           draw_sidebar (CurrentMenu);
521           mutt_message (_("New mail in this mailbox."));
522
523           if (option (OPTBEEPNEW))
524             beep ();
525         }
526         else if (check == M_FLAGS)
527           mutt_message (_("Mailbox was externally modified."));
528
529         /* avoid the message being overwritten by buffy */
530         do_buffy_notify = 0;
531
532         menu->redraw = REDRAW_FULL;
533         menu->max = Context->vcount;
534
535         set_option (OPTSEARCHINVALID);
536       }
537     }
538
539 #ifdef USE_IMAP
540     imap_keepalive ();
541     imap_disallow_reopen (Context);
542 #endif
543
544     if (!attach_msg) {
545       /* check for new mail in the incoming folders */
546       oldcount = newcount;
547       if ((newcount = mutt_buffy_check (0)) != oldcount) {
548         menu->redraw |= REDRAW_STATUS;
549         menu->redraw |= REDRAW_SIDEBAR;
550       }
551       if (do_buffy_notify) {
552         if (mutt_buffy_notify () && option (OPTBEEPNEW))
553           beep ();
554       }
555       else
556         do_buffy_notify = 1;
557     }
558
559     if (op != -1)
560       mutt_curs_set (0);
561     if (menu->redraw & REDRAW_SIDEBAR)
562       draw_sidebar (menu->menu);
563     if (menu->redraw & REDRAW_FULL) {
564       menu_redraw_full (menu);
565       draw_sidebar (menu->menu);
566       mutt_show_error ();
567     }
568
569     if (menu->menu == MENU_MAIN) {
570       if (Context && Context->hdrs && !(menu->current >= Context->vcount)) {
571         menu_check_recenter (menu);
572
573         if (menu->redraw & REDRAW_INDEX) {
574           menu_redraw_index (menu);
575           menu->redraw |= REDRAW_STATUS;
576         }
577         else if (menu->redraw & (REDRAW_MOTION_RESYNCH | REDRAW_MOTION))
578           menu_redraw_motion (menu);
579         else if (menu->redraw & REDRAW_CURRENT)
580           menu_redraw_current (menu);
581       }
582
583       if (menu->redraw & REDRAW_STATUS) {
584         DrawFullLine = 1;
585         menu_status_line (buf, sizeof (buf), menu, NONULL (Status));
586         DrawFullLine = 0;
587         CLEARLINE (option (OPTSTATUSONTOP) ? 0 : LINES - 2);
588         SETCOLOR (MT_COLOR_STATUS);
589         mutt_paddstr (COLS, buf);
590         SETCOLOR (MT_COLOR_NORMAL);
591         set_buffystats (Context);
592         menu->redraw &= ~REDRAW_STATUS;
593         if (option (OPTXTERMSETTITLES)) {
594           menu_status_line (buf, sizeof (buf), menu, NONULL (XtermTitle));
595           set_xterm_title_bar (buf);
596           menu_status_line (buf, sizeof (buf), menu, NONULL (XtermIcon));
597           set_xterm_icon_name (buf);
598         }
599       }
600
601       menu->redraw = 0;
602       if (menu->current < menu->max)
603         menu->oldcurrent = menu->current;
604       else
605         menu->oldcurrent = -1;
606
607       if (option (OPTARROWCURSOR))
608         move (menu->current - menu->top + menu->offset, 2);
609       else
610         move (menu->current - menu->top + menu->offset, COLS - 1);
611       mutt_refresh ();
612
613       op = km_dokey (MENU_MAIN);
614
615       dprint (4,
616               (debugfile, "mutt_index_menu[%d]: Got op %d\n", __LINE__, op));
617
618 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
619       if (SigWinch) {
620         mutt_flushinp ();
621         mutt_resize_screen ();
622         menu->redraw = REDRAW_FULL;
623         menu->menu = MENU_MAIN;
624         SigWinch = 0;
625         menu->top = 0;          /* so we scroll the right amount */
626         /*
627          * force a real complete redraw.  clrtobot() doesn't seem to be able
628          * to handle every case without this.
629          */
630         clearok (stdscr, TRUE);
631         continue;
632       }
633 #endif
634
635       if (op == -1)
636         continue;               /* either user abort or timeout */
637
638       mutt_curs_set (1);
639
640       /* special handling for the tag-prefix function */
641       if (op == OP_TAG_PREFIX) {
642         if (!Context) {
643           mutt_error (_("No mailbox is open."));
644
645           continue;
646         }
647
648         if (!Context->tagged) {
649           mutt_error (_("No tagged messages."));
650
651           continue;
652         }
653         tag = 1;
654
655         /* give visual indication that the next command is a tag- command */
656         mvaddstr (LINES - 1, 0, "tag-");
657         clrtoeol ();
658
659         /* get the real command */
660         if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX) {
661           /* abort tag sequence */
662           CLEARLINE (LINES - 1);
663           continue;
664         }
665       }
666       else if (option (OPTAUTOTAG) && Context && Context->tagged)
667         tag = 1;
668
669       if (op == OP_TAG_PREFIX_COND) {
670         if (!Context) {
671           mutt_error (_("No mailbox is open."));
672
673           continue;
674         }
675
676         if (!Context->tagged) {
677           event_t tmp;
678
679           while (UngetCount > 0) {
680             tmp = mutt_getch ();
681             if (tmp.op == OP_END_COND)
682               break;
683           }
684           mutt_message (_("Nothing to do."));
685
686           continue;
687         }
688         tag = 1;
689
690         /* give visual indication that the next command is a tag- command */
691         mvaddstr (LINES - 1, 0, "tag-");
692         clrtoeol ();
693
694         /* get the real command */
695         if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX) {
696           /* abort tag sequence */
697           CLEARLINE (LINES - 1);
698           continue;
699         }
700       }
701
702       mutt_clear_error ();
703     }
704     else {
705       if (menu->current < menu->max)
706         menu->oldcurrent = menu->current;
707       else
708         menu->oldcurrent = -1;
709
710       mutt_curs_set (1);        /* fallback from the pager */
711     }
712
713 #ifdef USE_NNTP
714     unset_option (OPTNEWS);     /* for any case */
715 #endif
716
717     switch (op) {
718
719       /* ----------------------------------------------------------------------
720        * movement commands
721        */
722
723     case OP_BOTTOM_PAGE:
724       menu_bottom_page (menu);
725       break;
726     case OP_FIRST_ENTRY:
727       menu_first_entry (menu);
728       break;
729     case OP_MIDDLE_PAGE:
730       menu_middle_page (menu);
731       break;
732     case OP_HALF_UP:
733       menu_half_up (menu);
734       break;
735     case OP_HALF_DOWN:
736       menu_half_down (menu);
737       break;
738     case OP_NEXT_LINE:
739       menu_next_line (menu);
740       break;
741     case OP_PREV_LINE:
742       menu_prev_line (menu);
743       break;
744     case OP_NEXT_PAGE:
745       menu_next_page (menu);
746       break;
747     case OP_PREV_PAGE:
748       menu_prev_page (menu);
749       break;
750     case OP_LAST_ENTRY:
751       menu_last_entry (menu);
752       break;
753     case OP_TOP_PAGE:
754       menu_top_page (menu);
755       break;
756     case OP_CURRENT_TOP:
757       menu_current_top (menu);
758       break;
759     case OP_CURRENT_MIDDLE:
760       menu_current_middle (menu);
761       break;
762     case OP_CURRENT_BOTTOM:
763       menu_current_bottom (menu);
764       break;
765
766 #ifdef USE_NNTP
767     case OP_GET_MESSAGE:
768     case OP_GET_PARENT:
769       CHECK_MSGCOUNT;
770       if (Context->magic == M_NNTP) {
771         HEADER *h;
772
773         if (op == OP_GET_MESSAGE) {
774           buf[0] = 0;
775           if (mutt_get_field (_("Enter Message-Id: "), buf, sizeof (buf), 0)
776               != 0 || !buf[0])
777             break;
778         }
779         else {
780           LIST *ref = CURHDR->env->references;
781
782           if (!ref) {
783             mutt_error (_("Article has no parent reference!"));
784
785             break;
786           }
787           strfcpy (buf, ref->data, sizeof (buf));
788         }
789         if (!Context->id_hash)
790           Context->id_hash = mutt_make_id_hash (Context);
791         if ((h = hash_find (Context->id_hash, buf))) {
792           if (h->virtual != -1) {
793             menu->current = h->virtual;
794             menu->redraw = REDRAW_MOTION_RESYNCH;
795           }
796           else if (h->collapsed) {
797             mutt_uncollapse_thread (Context, h);
798             mutt_set_virtual (Context);
799             menu->current = h->virtual;
800             menu->redraw = REDRAW_MOTION_RESYNCH;
801           }
802           else
803             mutt_error (_("Message not visible in limited view."));
804         }
805         else {
806           if (nntp_check_msgid (Context, buf) == 0) {
807             h = Context->hdrs[Context->msgcount - 1];
808             mutt_sort_headers (Context, 0);
809             menu->current = h->virtual;
810             menu->redraw = REDRAW_FULL;
811           }
812           else
813             mutt_error (_("Article %s not found on server"), buf);
814         }
815       }
816       break;
817
818     case OP_GET_CHILDREN:
819     case OP_RECONSTRUCT_THREAD:
820       CHECK_MSGCOUNT;
821       if (Context->magic == M_NNTP) {
822         HEADER *h;
823         int old = CURHDR->index, i;
824
825         if (!CURHDR->env->message_id) {
826           mutt_error (_("No Message-Id. Unable to perform operation"));
827
828           break;
829         }
830
831         if (!Context->id_hash)
832           Context->id_hash = mutt_make_id_hash (Context);
833         strfcpy (buf, CURHDR->env->message_id, sizeof (buf));
834
835         if (op == OP_RECONSTRUCT_THREAD) {
836           LIST *ref = CURHDR->env->references;
837
838           while (ref) {
839             nntp_check_msgid (Context, ref->data);
840             /* the last msgid in References is the root message */
841             if (!ref->next)
842               strfcpy (buf, ref->data, sizeof (buf));
843             ref = ref->next;
844           }
845         }
846         mutt_message (_("Check for children of message..."));
847
848         if (nntp_check_children (Context, buf) == 0) {
849           mutt_sort_headers (Context, (op == OP_RECONSTRUCT_THREAD));
850           h = hash_find (Context->id_hash, buf);
851           /* if the root message was retrieved, move to it */
852           if (h)
853             menu->current = h->virtual;
854           else                  /* try to restore old position */
855             for (i = 0; i < Context->msgcount; i++)
856               if (Context->hdrs[i]->index == old) {
857                 menu->current = Context->hdrs[i]->virtual;
858                 /* As an added courtesy, recenter the menu
859                  * with the current entry at the middle of the screen */
860                 menu_check_recenter (menu);
861                 menu_current_middle (menu);
862               }
863         }
864         menu->redraw = REDRAW_FULL;
865         mutt_clear_error ();
866       }
867       break;
868 #endif
869
870     case OP_JUMP:
871
872       CHECK_MSGCOUNT;
873       CHECK_VISIBLE;
874       if (isdigit (LastKey))
875         mutt_ungetch (LastKey, 0);
876       buf[0] = 0;
877       if (mutt_get_field (_("Jump to message: "), buf, sizeof (buf), 0) != 0
878           || !buf[0])
879         break;
880
881       if (!isdigit ((unsigned char) buf[0])) {
882         mutt_error (_("Argument must be a message number."));
883
884         break;
885       }
886
887       i = atoi (buf);
888       if (i > 0 && i <= Context->msgcount) {
889         for (j = i - 1; j < Context->msgcount; j++) {
890           if (Context->hdrs[j]->virtual != -1)
891             break;
892         }
893         if (j >= Context->msgcount) {
894           for (j = i - 2; j >= 0; j--) {
895             if (Context->hdrs[j]->virtual != -1)
896               break;
897           }
898         }
899
900         if (j >= 0) {
901           menu->current = Context->hdrs[j]->virtual;
902           if (menu->menu == MENU_PAGER) {
903             op = OP_DISPLAY_MESSAGE;
904             continue;
905           }
906           else
907             menu->redraw = REDRAW_MOTION;
908         }
909         else
910           mutt_error (_("That message is not visible."));
911       }
912       else
913         mutt_error (_("Invalid message number."));
914
915       break;
916
917       /* --------------------------------------------------------------------
918        * `index' specific commands
919        */
920
921     case OP_MAIN_DELETE_PATTERN:
922
923       CHECK_MSGCOUNT;
924       CHECK_VISIBLE;
925       CHECK_READONLY;
926
927 #ifdef USE_IMAP
928       CHECK_IMAP_ACL (IMAP_ACL_DELETE);
929 #endif
930
931       CHECK_ATTACH;
932       mutt_pattern_func (M_DELETE, _("Delete messages matching: "));
933       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
934       break;
935
936 #ifdef USE_POP
937     case OP_MAIN_FETCH_MAIL:
938
939       CHECK_ATTACH;
940       pop_fetch_mail ();
941       menu->redraw = REDRAW_FULL;
942       break;
943 #endif /* USE_POP */
944
945     case OP_HELP:
946
947       mutt_help (MENU_MAIN);
948       menu->redraw = REDRAW_FULL;
949       break;
950
951     case OP_MAIN_SHOW_LIMIT:
952       CHECK_MSGCOUNT;
953       if (!Context->pattern)
954         mutt_message (_("No limit pattern is in effect."));
955
956       else {
957         char buf[STRING];
958
959         /* i18n: ask for a limit to apply */
960         snprintf (buf, sizeof (buf), _("Limit: %s"), Context->pattern);
961         mutt_message ("%s", buf);
962       }
963       break;
964
965     case OP_MAIN_LIMIT:
966     case OP_TOGGLE_READ:
967
968       CHECK_MSGCOUNT;
969       menu->oldcurrent = (Context->vcount && menu->current >= 0
970                           && menu->current <
971                           Context->vcount) ? CURHDR->index : -1;
972       if (op == OP_TOGGLE_READ) {
973         char buf[LONG_STRING];
974
975         if (!Context->pattern
976             || strncmp (Context->pattern, "!~R!~D~s", 8) != 0) {
977           snprintf (buf, sizeof (buf), "!~R!~D~s%s",
978                     Context->pattern ? Context->pattern : ".*");
979           set_option (OPTHIDEREAD);
980         }
981         else {
982           strfcpy (buf, Context->pattern + 8, sizeof (buf));
983           if (!*buf || strncmp (buf, ".*", 2) == 0)
984             snprintf (buf, sizeof (buf), "~A");
985           unset_option (OPTHIDEREAD);
986         }
987         FREE (&Context->pattern);
988         Context->pattern = safe_strdup (buf);
989       }
990       if ((op == OP_TOGGLE_READ && mutt_pattern_func (M_LIMIT, NULL) == 0) ||
991           mutt_pattern_func (M_LIMIT, _("Limit to messages matching: ")) == 0)
992       {
993         if (menu->oldcurrent >= 0) {
994           /* try to find what used to be the current message */
995           menu->current = -1;
996           for (i = 0; i < Context->vcount; i++)
997             if (Context->hdrs[Context->v2r[i]]->index == menu->oldcurrent) {
998               menu->current = i;
999               break;
1000             }
1001           if (menu->current < 0)
1002             menu->current = 0;
1003         }
1004         else
1005           menu->current = 0;
1006         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1007         if ((Sort & SORT_MASK) == SORT_THREADS)
1008           mutt_draw_tree (Context);
1009         menu->redraw = REDRAW_FULL;
1010       }
1011       break;
1012
1013     case OP_QUIT:
1014
1015       close = op;
1016       if (attach_msg) {
1017         done = 1;
1018         break;
1019       }
1020
1021       if (query_quadoption (OPT_QUIT, _("Quit Mutt-ng?")) == M_YES) {
1022         int check;
1023
1024         oldcount = Context ? Context->msgcount : 0;
1025
1026         if (!Context
1027             || (check = mx_close_mailbox (Context, &index_hint)) == 0)
1028           done = 1;
1029         else {
1030           if (check == M_NEW_MAIL || check == M_REOPENED)
1031             update_index (menu, Context, check, oldcount, index_hint);
1032
1033           menu->redraw = REDRAW_FULL;   /* new mail arrived? */
1034           set_option (OPTSEARCHINVALID);
1035         }
1036       }
1037       break;
1038
1039     case OP_REDRAW:
1040
1041       clearok (stdscr, TRUE);
1042       menu->redraw = REDRAW_FULL;
1043       break;
1044
1045     case OP_SEARCH:
1046     case OP_SEARCH_REVERSE:
1047     case OP_SEARCH_NEXT:
1048     case OP_SEARCH_OPPOSITE:
1049
1050       CHECK_MSGCOUNT;
1051       CHECK_VISIBLE;
1052       if ((menu->current = mutt_search_command (menu->current, op)) == -1)
1053         menu->current = menu->oldcurrent;
1054       else
1055         menu->redraw = REDRAW_MOTION;
1056       break;
1057
1058     case OP_SORT:
1059     case OP_SORT_REVERSE:
1060
1061       if (mutt_select_sort ((op == OP_SORT_REVERSE)) == 0) {
1062         if (Context && Context->msgcount) {
1063           resort_index (menu);
1064           set_option (OPTSEARCHINVALID);
1065         }
1066       }
1067       break;
1068
1069     case OP_TAG:
1070
1071       CHECK_MSGCOUNT;
1072       CHECK_VISIBLE;
1073       if (tag && !option (OPTAUTOTAG)) {
1074         for (j = 0; j < Context->vcount; j++)
1075           mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_TAG, 0);
1076         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1077       }
1078       else {
1079         mutt_set_flag (Context, CURHDR, M_TAG, !CURHDR->tagged);
1080         Context->last_tag = CURHDR->tagged ? CURHDR :
1081           ((Context->last_tag == CURHDR && !CURHDR->tagged)
1082            ? NULL : Context->last_tag);
1083         menu->redraw = REDRAW_STATUS;
1084         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
1085           menu->current++;
1086           menu->redraw |= REDRAW_MOTION_RESYNCH;
1087         }
1088         else
1089           menu->redraw |= REDRAW_CURRENT;
1090       }
1091       break;
1092
1093     case OP_MAIN_TAG_PATTERN:
1094
1095       CHECK_MSGCOUNT;
1096       CHECK_VISIBLE;
1097       mutt_pattern_func (M_TAG, _("Tag messages matching: "));
1098       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1099       break;
1100
1101     case OP_MAIN_UNDELETE_PATTERN:
1102
1103       CHECK_MSGCOUNT;
1104       CHECK_VISIBLE;
1105       CHECK_READONLY;
1106
1107 #ifdef USE_IMAP
1108       CHECK_IMAP_ACL (IMAP_ACL_DELETE);
1109 #endif
1110
1111       if (mutt_pattern_func (M_UNDELETE, _("Undelete messages matching: ")) ==
1112           0)
1113         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1114       break;
1115
1116     case OP_MAIN_UNTAG_PATTERN:
1117
1118       CHECK_MSGCOUNT;
1119       CHECK_VISIBLE;
1120       if (mutt_pattern_func (M_UNTAG, _("Untag messages matching: ")) == 0)
1121         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1122       break;
1123
1124       /* --------------------------------------------------------------------
1125        * The following operations can be performed inside of the pager.
1126        */
1127
1128 #ifdef USE_IMAP
1129     case OP_MAIN_IMAP_FETCH:
1130       if (Context->magic == M_IMAP)
1131         imap_check_mailbox (Context, &index_hint, 1);
1132       break;
1133 #endif
1134
1135     case OP_MAIN_SYNC_FOLDER:
1136
1137       if (Context && !Context->msgcount)
1138         break;
1139
1140       CHECK_MSGCOUNT;
1141       CHECK_VISIBLE;
1142       CHECK_READONLY;
1143       {
1144         int oldvcount = Context->vcount;
1145         int oldcount = Context->msgcount;
1146         int dcount = 0;
1147         int check;
1148
1149         /* calculate the number of messages _above_ the cursor,
1150          * so we can keep the cursor on the current message
1151          */
1152         for (j = 0; j <= menu->current; j++) {
1153           if (Context->hdrs[Context->v2r[j]]->deleted)
1154             dcount++;
1155         }
1156
1157         if ((check = mx_sync_mailbox (Context, &index_hint)) == 0) {
1158           if (Context->vcount != oldvcount)
1159             menu->current -= dcount;
1160           set_option (OPTSEARCHINVALID);
1161         }
1162         else if (check == M_NEW_MAIL || check == M_REOPENED)
1163           update_index (menu, Context, check, oldcount, index_hint);
1164
1165         /* 
1166          * do a sanity check even if mx_sync_mailbox failed.
1167          */
1168
1169         if (menu->current < 0 || menu->current >= Context->vcount)
1170           menu->current = ci_first_message ();
1171       }
1172
1173       /* check for a fatal error, or all messages deleted */
1174       if (!Context->path)
1175         FREE (&Context);
1176
1177       /* if we were in the pager, redisplay the message */
1178       if (menu->menu == MENU_PAGER) {
1179         op = OP_DISPLAY_MESSAGE;
1180         continue;
1181       }
1182       else
1183         menu->redraw = REDRAW_FULL;
1184       break;
1185
1186     case OP_SIDEBAR_OPEN:
1187     case OP_MAIN_CHANGE_FOLDER:
1188     case OP_MAIN_CHANGE_FOLDER_READONLY:
1189 #ifdef USE_NNTP
1190     case OP_MAIN_CHANGE_GROUP:
1191     case OP_MAIN_CHANGE_GROUP_READONLY:
1192 #endif
1193       if (attach_msg || option (OPTREADONLY) ||
1194 #ifdef USE_NNTP
1195           op == OP_MAIN_CHANGE_GROUP_READONLY ||
1196 #endif
1197           op == OP_MAIN_CHANGE_FOLDER_READONLY)
1198         flags = M_READONLY;
1199       else
1200         flags = 0;
1201
1202       if (flags)
1203         cp = _("Open mailbox in read-only mode");
1204       else
1205         cp = _("Open mailbox");
1206
1207       buf[0] = '\0';
1208 #ifdef USE_NNTP
1209       unset_option (OPTNEWS);
1210       if (op == OP_MAIN_CHANGE_GROUP || op == OP_MAIN_CHANGE_GROUP_READONLY) {
1211         set_option (OPTNEWS);
1212         if (!(CurrentNewsSrv = mutt_select_newsserver (NewsServer)))
1213           break;
1214         if (flags)
1215           cp = _("Open newsgroup in read-only mode");
1216         else
1217           cp = _("Open newsgroup");
1218         nntp_buffy (buf);
1219       }
1220       else
1221 #endif
1222       {
1223         if (Context && Context->path)
1224           strncpy (buf, Context->path, sizeof (buf));
1225         mutt_buffy (buf, sizeof (buf));
1226       }
1227
1228       if (op == OP_SIDEBAR_OPEN) {
1229         if (!CurBuffy)
1230           break;
1231         strncpy (buf, CurBuffy->path, sizeof (buf));
1232       }
1233       else if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) ==
1234                -1)
1235         break;
1236       if (!buf[0]) {
1237         CLEARLINE (LINES - 1);
1238         break;
1239       }
1240
1241 #ifdef USE_NNTP
1242       if (option (OPTNEWS)) {
1243         unset_option (OPTNEWS);
1244         nntp_expand_path (buf, sizeof (buf), &CurrentNewsSrv->conn->account);
1245       }
1246       else
1247 #endif
1248         mutt_expand_path (buf, sizeof (buf));
1249       set_curbuffy (buf);
1250       if (mx_get_magic (buf) <= 0) {
1251         mutt_error (_("%s is not a mailbox."), buf);
1252         break;
1253       }
1254       mutt_str_replace (&CurrentFolder, buf);
1255
1256       if (Context) {
1257         int check;
1258
1259 #ifdef USE_COMPRESSED
1260         if (Context->compressinfo && Context->realpath)
1261           mutt_str_replace (&LastFolder, Context->realpath);
1262         else
1263 #endif
1264
1265           mutt_str_replace (&LastFolder, Context->path);
1266         oldcount = Context ? Context->msgcount : 0;
1267
1268         if ((check = mx_close_mailbox (Context, &index_hint)) != 0) {
1269           if (check == M_NEW_MAIL || check == M_REOPENED)
1270             update_index (menu, Context, check, oldcount, index_hint);
1271
1272           set_option (OPTSEARCHINVALID);
1273           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1274           break;
1275         }
1276         FREE (&Context);
1277       }
1278
1279       mutt_sleep (0);
1280
1281       /* Set CurrentMenu to MENU_MAIN before executing any folder
1282        * hooks so that all the index menu functions are available to
1283        * the exec command.
1284        */
1285
1286       CurrentMenu = MENU_MAIN;
1287       mutt_folder_hook (buf);
1288
1289       if ((Context = mx_open_mailbox (buf, flags, NULL)) != NULL) {
1290         menu->current = ci_first_message ();
1291       }
1292       else
1293         menu->current = 0;
1294
1295 #ifdef USE_NNTP
1296       /* mutt_buffy_check() must be done with mail-reader mode! */
1297       menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN,
1298                                       (Context
1299                                        && (Context->magic ==
1300                                            M_NNTP)) ? IndexNewsHelp :
1301                                       IndexHelp);
1302 #endif
1303       mutt_clear_error ();
1304       mutt_buffy_check (1);     /* force the buffy check after we have changed
1305                                    the folder */
1306       menu->redraw = REDRAW_FULL;
1307       set_option (OPTSEARCHINVALID);
1308       break;
1309
1310     case OP_DISPLAY_MESSAGE:
1311     case OP_DISPLAY_HEADERS:   /* don't weed the headers */
1312
1313       CHECK_MSGCOUNT;
1314       CHECK_VISIBLE;
1315       /*
1316        * toggle the weeding of headers so that a user can press the key
1317        * again while reading the message.
1318        */
1319       if (op == OP_DISPLAY_HEADERS)
1320         toggle_option (OPTWEED);
1321
1322       unset_option (OPTNEEDRESORT);
1323
1324       if ((Sort & SORT_MASK) == SORT_THREADS && CURHDR->collapsed) {
1325         mutt_uncollapse_thread (Context, CURHDR);
1326         mutt_set_virtual (Context);
1327         if (option (OPTUNCOLLAPSEJUMP))
1328           menu->current = mutt_thread_next_unread (Context, CURHDR);
1329       }
1330
1331       if ((op = mutt_display_message (CURHDR)) == -1) {
1332         unset_option (OPTNEEDRESORT);
1333         break;
1334       }
1335
1336       menu->menu = MENU_PAGER;
1337       menu->oldcurrent = menu->current;
1338       continue;
1339
1340     case OP_EXIT:
1341
1342       close = op;
1343       if (menu->menu == MENU_MAIN && attach_msg) {
1344         done = 1;
1345         break;
1346       }
1347
1348       if ((menu->menu == MENU_MAIN)
1349           && (query_quadoption (OPT_QUIT,
1350                                 _("Exit Mutt-ng without saving?")) == M_YES))
1351       {
1352         if (Context) {
1353           mx_fastclose_mailbox (Context);
1354           FREE (&Context);
1355         }
1356         done = 1;
1357       }
1358       break;
1359
1360     case OP_EDIT_TYPE:
1361
1362       CHECK_MSGCOUNT;
1363       CHECK_VISIBLE;
1364       CHECK_ATTACH;
1365       mutt_edit_content_type (CURHDR, CURHDR->content, NULL);
1366       /* if we were in the pager, redisplay the message */
1367       if (menu->menu == MENU_PAGER) {
1368         op = OP_DISPLAY_MESSAGE;
1369         continue;
1370       }
1371       else
1372         menu->redraw = REDRAW_CURRENT;
1373       break;
1374
1375     case OP_MAIN_BREAK_THREAD:
1376
1377       CHECK_MSGCOUNT;
1378       CHECK_VISIBLE;
1379       CHECK_READONLY;
1380
1381       if ((Sort & SORT_MASK) != SORT_THREADS)
1382         mutt_error (_("Threading is not enabled."));
1383
1384       else {
1385         {
1386           HEADER *oldcur = CURHDR;
1387
1388           mutt_break_thread (CURHDR);
1389           mutt_sort_headers (Context, 1);
1390           menu->current = oldcur->virtual;
1391         }
1392
1393         Context->changed = 1;
1394         mutt_message _("Thread broken");
1395
1396         if (menu->menu == MENU_PAGER) {
1397           op = OP_DISPLAY_MESSAGE;
1398           continue;
1399         }
1400         else
1401           menu->redraw |= REDRAW_INDEX;
1402       }
1403       break;
1404
1405     case OP_MAIN_LINK_THREADS:
1406
1407       CHECK_MSGCOUNT;
1408       CHECK_VISIBLE;
1409       CHECK_READONLY;
1410
1411       if ((Sort & SORT_MASK) != SORT_THREADS)
1412         mutt_error (_("Threading is not enabled."));
1413
1414       else if (!CURHDR->env->message_id)
1415         mutt_error (_("No Message-ID: header available to link thread"));
1416
1417       else if (!tag && (!Context->last_tag || !Context->last_tag->tagged))
1418         mutt_error (_("First, please tag a message to be linked here"));
1419
1420       else {
1421         HEADER *oldcur = CURHDR;
1422
1423         if (mutt_link_threads (CURHDR, tag ? NULL : Context->last_tag,
1424                                Context)) {
1425           mutt_sort_headers (Context, 1);
1426           menu->current = oldcur->virtual;
1427
1428           Context->changed = 1;
1429           mutt_message _("Threads linked");
1430         }
1431         else
1432           mutt_error (_("No thread linked"));
1433       }
1434
1435       if (menu->menu == MENU_PAGER) {
1436         op = OP_DISPLAY_MESSAGE;
1437         continue;
1438       }
1439       else
1440         menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
1441       break;
1442
1443     case OP_MAIN_NEXT_UNDELETED:
1444
1445       CHECK_MSGCOUNT;
1446       CHECK_VISIBLE;
1447       if (menu->current >= Context->vcount - 1) {
1448         if (menu->menu == MENU_MAIN)
1449           mutt_error (_("You are on the last message."));
1450
1451         break;
1452       }
1453       if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1454         menu->current = menu->oldcurrent;
1455         if (menu->menu == MENU_MAIN)
1456           mutt_error (_("No undeleted messages."));
1457       }
1458       else if (menu->menu == MENU_PAGER) {
1459         op = OP_DISPLAY_MESSAGE;
1460         continue;
1461       }
1462       else
1463         menu->redraw = REDRAW_MOTION;
1464       break;
1465
1466     case OP_NEXT_ENTRY:
1467
1468       CHECK_MSGCOUNT;
1469       CHECK_VISIBLE;
1470       if (menu->current >= Context->vcount - 1) {
1471         if (menu->menu == MENU_MAIN)
1472           mutt_error (_("You are on the last message."));
1473
1474         break;
1475       }
1476       menu->current++;
1477       if (menu->menu == MENU_PAGER) {
1478         op = OP_DISPLAY_MESSAGE;
1479         continue;
1480       }
1481       else
1482         menu->redraw = REDRAW_MOTION;
1483       break;
1484
1485     case OP_MAIN_PREV_UNDELETED:
1486
1487       CHECK_MSGCOUNT;
1488       CHECK_VISIBLE;
1489       if (menu->current < 1) {
1490         mutt_error (_("You are on the first message."));
1491
1492         break;
1493       }
1494       if ((menu->current = ci_previous_undeleted (menu->current)) == -1) {
1495         menu->current = menu->oldcurrent;
1496         if (menu->menu == MENU_MAIN)
1497           mutt_error (_("No undeleted messages."));
1498       }
1499       else if (menu->menu == MENU_PAGER) {
1500         op = OP_DISPLAY_MESSAGE;
1501         continue;
1502       }
1503       else
1504         menu->redraw = REDRAW_MOTION;
1505       break;
1506
1507     case OP_PREV_ENTRY:
1508
1509       CHECK_MSGCOUNT;
1510       CHECK_VISIBLE;
1511       if (menu->current < 1) {
1512         if (menu->menu == MENU_MAIN)
1513           mutt_error (_("You are on the first message."));
1514
1515         break;
1516       }
1517       menu->current--;
1518       if (menu->menu == MENU_PAGER) {
1519         op = OP_DISPLAY_MESSAGE;
1520         continue;
1521       }
1522       else
1523         menu->redraw = REDRAW_MOTION;
1524       break;
1525
1526     case OP_DECRYPT_COPY:
1527     case OP_DECRYPT_SAVE:
1528       if (!WithCrypto)
1529         break;
1530       /* fall thru */
1531     case OP_COPY_MESSAGE:
1532     case OP_SAVE:
1533     case OP_DECODE_COPY:
1534     case OP_DECODE_SAVE:
1535       CHECK_MSGCOUNT;
1536       CHECK_VISIBLE;
1537       if (mutt_save_message (tag ? NULL : CURHDR,
1538                              (op == OP_DECRYPT_SAVE) ||
1539                              (op == OP_SAVE) || (op == OP_DECODE_SAVE),
1540                              (op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY),
1541                              (op == OP_DECRYPT_SAVE)
1542                              || (op == OP_DECRYPT_COPY)
1543                              || 0, &menu->redraw) == 0 && (op == OP_SAVE
1544                                                            || op ==
1545                                                            OP_DECODE_SAVE
1546                                                            || op ==
1547                                                            OP_DECRYPT_SAVE)
1548         ) {
1549         if (tag)
1550           menu->redraw |= REDRAW_INDEX;
1551         else if (option (OPTRESOLVE)) {
1552           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1553             menu->current = menu->oldcurrent;
1554             menu->redraw |= REDRAW_CURRENT;
1555           }
1556           else
1557             menu->redraw |= REDRAW_MOTION_RESYNCH;
1558         }
1559         else
1560           menu->redraw |= REDRAW_CURRENT;
1561       }
1562       break;
1563
1564     case OP_MAIN_NEXT_NEW:
1565     case OP_MAIN_NEXT_UNREAD:
1566     case OP_MAIN_PREV_NEW:
1567     case OP_MAIN_PREV_UNREAD:
1568     case OP_MAIN_NEXT_NEW_THEN_UNREAD:
1569     case OP_MAIN_PREV_NEW_THEN_UNREAD:
1570
1571       {
1572         int first_unread = -1;
1573         int first_new = -1;
1574
1575         CHECK_MSGCOUNT;
1576         CHECK_VISIBLE;
1577
1578         i = menu->current;
1579         menu->current = -1;
1580         for (j = 0; j != Context->vcount; j++) {
1581 #define CURHDRi Context->hdrs[Context->v2r[i]]
1582           if (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_NEXT_UNREAD
1583               || op == OP_MAIN_NEXT_NEW_THEN_UNREAD) {
1584             i++;
1585             if (i > Context->vcount - 1) {
1586               mutt_message _("Search wrapped to top.");
1587
1588               i = 0;
1589             }
1590           }
1591           else {
1592             i--;
1593             if (i < 0) {
1594               mutt_message _("Search wrapped to bottom.");
1595
1596               i = Context->vcount - 1;
1597             }
1598           }
1599
1600           if (CURHDRi->collapsed && (Sort & SORT_MASK) == SORT_THREADS) {
1601             if (UNREAD (CURHDRi) && first_unread == -1)
1602               first_unread = i;
1603             if (UNREAD (CURHDRi) == 1 && first_new == -1)
1604               first_new = i;
1605           }
1606           else if ((!CURHDRi->deleted && !CURHDRi->read)) {
1607             if (first_unread == -1)
1608               first_unread = i;
1609             if ((!CURHDRi->old) && first_new == -1)
1610               first_new = i;
1611           }
1612
1613           if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD) &&
1614               first_unread != -1)
1615             break;
1616           if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1617                op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1618                || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1619               && first_new != -1)
1620             break;
1621         }
1622 #undef CURHDRi
1623         if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1624              op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1625              || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1626             && first_new != -1)
1627           menu->current = first_new;
1628         else if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD ||
1629                   op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1630                   || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1631                  && first_unread != -1)
1632           menu->current = first_unread;
1633
1634         if (menu->current == -1) {
1635           menu->current = menu->oldcurrent;
1636           mutt_error ("%s%s.",
1637                       (op == OP_MAIN_NEXT_NEW
1638                        || op ==
1639                        OP_MAIN_PREV_NEW) ? _("No new messages") :
1640                       _("No unread messages"),
1641                       Context->pattern ? _(" in this limited view") : "");
1642         }
1643         else if (menu->menu == MENU_PAGER) {
1644           op = OP_DISPLAY_MESSAGE;
1645           continue;
1646         }
1647         else
1648           menu->redraw = REDRAW_MOTION;
1649         break;
1650       }
1651     case OP_FLAG_MESSAGE:
1652
1653       CHECK_MSGCOUNT;
1654       CHECK_VISIBLE;
1655       CHECK_READONLY;
1656
1657 #ifdef USE_POP
1658       if (Context->magic == M_POP) {
1659         mutt_flushinp ();
1660         mutt_error (_("Can't change 'important' flag on POP server."));
1661
1662         break;
1663       }
1664 #endif
1665
1666 #ifdef USE_IMAP
1667       CHECK_IMAP_ACL (IMAP_ACL_WRITE);
1668 #endif
1669
1670 #ifdef USE_NNTP
1671       if (Context->magic == M_NNTP) {
1672         mutt_flushinp ();
1673         mutt_error (_("Can't change 'important' flag on NNTP server."));
1674
1675         break;
1676       }
1677 #endif
1678
1679       if (tag) {
1680         for (j = 0; j < Context->vcount; j++) {
1681           if (Context->hdrs[Context->v2r[j]]->tagged)
1682             mutt_set_flag (Context, Context->hdrs[Context->v2r[j]],
1683                            M_FLAG, !Context->hdrs[Context->v2r[j]]->flagged);
1684         }
1685
1686         menu->redraw |= REDRAW_INDEX;
1687       }
1688       else {
1689         mutt_set_flag (Context, CURHDR, M_FLAG, !CURHDR->flagged);
1690         if (option (OPTRESOLVE)) {
1691           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1692             menu->current = menu->oldcurrent;
1693             menu->redraw = REDRAW_CURRENT;
1694           }
1695           else
1696             menu->redraw = REDRAW_MOTION_RESYNCH;
1697         }
1698         else
1699           menu->redraw = REDRAW_CURRENT;
1700       }
1701       menu->redraw |= REDRAW_STATUS;
1702       break;
1703
1704     case OP_TOGGLE_NEW:
1705
1706       CHECK_MSGCOUNT;
1707       CHECK_VISIBLE;
1708       CHECK_READONLY;
1709
1710 #ifdef USE_IMAP
1711       CHECK_IMAP_ACL (IMAP_ACL_SEEN);
1712 #endif
1713
1714       if (tag) {
1715         for (j = 0; j < Context->vcount; j++) {
1716           if (Context->hdrs[Context->v2r[j]]->tagged) {
1717             if (Context->hdrs[Context->v2r[j]]->read ||
1718                 Context->hdrs[Context->v2r[j]]->old)
1719               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_NEW,
1720                              1);
1721             else
1722               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_READ,
1723                              1);
1724           }
1725         }
1726         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1727       }
1728       else {
1729         if (CURHDR->read || CURHDR->old)
1730           mutt_set_flag (Context, CURHDR, M_NEW, 1);
1731         else
1732           mutt_set_flag (Context, CURHDR, M_READ, 1);
1733
1734         if (option (OPTRESOLVE)) {
1735           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1736             menu->current = menu->oldcurrent;
1737             menu->redraw = REDRAW_CURRENT;
1738           }
1739           else
1740             menu->redraw = REDRAW_MOTION_RESYNCH;
1741         }
1742         else
1743           menu->redraw = REDRAW_CURRENT;
1744         menu->redraw |= REDRAW_STATUS;
1745       }
1746       break;
1747
1748     case OP_TOGGLE_WRITE:
1749
1750       CHECK_MSGCOUNT;
1751       if (mx_toggle_write (Context) == 0)
1752         menu->redraw |= REDRAW_STATUS;
1753       break;
1754
1755     case OP_MAIN_NEXT_THREAD:
1756     case OP_MAIN_NEXT_SUBTHREAD:
1757     case OP_MAIN_PREV_THREAD:
1758     case OP_MAIN_PREV_SUBTHREAD:
1759
1760       CHECK_MSGCOUNT;
1761       CHECK_VISIBLE;
1762       switch (op) {
1763       case OP_MAIN_NEXT_THREAD:
1764         menu->current = mutt_next_thread (CURHDR);
1765         break;
1766
1767       case OP_MAIN_NEXT_SUBTHREAD:
1768         menu->current = mutt_next_subthread (CURHDR);
1769         break;
1770
1771       case OP_MAIN_PREV_THREAD:
1772         menu->current = mutt_previous_thread (CURHDR);
1773         break;
1774
1775       case OP_MAIN_PREV_SUBTHREAD:
1776         menu->current = mutt_previous_subthread (CURHDR);
1777         break;
1778       }
1779
1780       if (menu->current < 0) {
1781         menu->current = menu->oldcurrent;
1782         if (op == OP_MAIN_NEXT_THREAD || op == OP_MAIN_NEXT_SUBTHREAD)
1783           mutt_error (_("No more threads."));
1784
1785         else
1786           mutt_error (_("You are on the first thread."));
1787       }
1788       else if (menu->menu == MENU_PAGER) {
1789         op = OP_DISPLAY_MESSAGE;
1790         continue;
1791       }
1792       else
1793         menu->redraw = REDRAW_MOTION;
1794       break;
1795
1796     case OP_MAIN_PARENT_MESSAGE:
1797
1798       CHECK_MSGCOUNT;
1799       CHECK_VISIBLE;
1800
1801       if ((menu->current = mutt_parent_message (Context, CURHDR)) < 0) {
1802         menu->current = menu->oldcurrent;
1803       }
1804       else if (menu->menu == MENU_PAGER) {
1805         op = OP_DISPLAY_MESSAGE;
1806         continue;
1807       }
1808       else
1809         menu->redraw = REDRAW_MOTION;
1810       break;
1811
1812     case OP_MAIN_SET_FLAG:
1813     case OP_MAIN_CLEAR_FLAG:
1814
1815       CHECK_MSGCOUNT;
1816       CHECK_VISIBLE;
1817       CHECK_READONLY;
1818
1819 /* #ifdef USE_IMAP
1820 CHECK_IMAP_ACL(IMAP_ACL_WRITE);
1821 #endif */
1822
1823       if (mutt_change_flag (tag ? NULL : CURHDR, (op == OP_MAIN_SET_FLAG)) ==
1824           0) {
1825         menu->redraw = REDRAW_STATUS;
1826         if (tag)
1827           menu->redraw |= REDRAW_INDEX;
1828         else if (option (OPTRESOLVE)) {
1829           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1830             menu->current = menu->oldcurrent;
1831             menu->redraw |= REDRAW_CURRENT;
1832           }
1833           else
1834             menu->redraw |= REDRAW_MOTION_RESYNCH;
1835         }
1836         else
1837           menu->redraw |= REDRAW_CURRENT;
1838       }
1839       break;
1840
1841     case OP_MAIN_COLLAPSE_THREAD:
1842       CHECK_MSGCOUNT;
1843       CHECK_VISIBLE;
1844
1845       if ((Sort & SORT_MASK) != SORT_THREADS) {
1846         mutt_error (_("Threading is not enabled."));
1847
1848         break;
1849       }
1850
1851       if (CURHDR->collapsed) {
1852         menu->current = mutt_uncollapse_thread (Context, CURHDR);
1853         mutt_set_virtual (Context);
1854         if (option (OPTUNCOLLAPSEJUMP))
1855           menu->current = mutt_thread_next_unread (Context, CURHDR);
1856       }
1857       else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR)) {
1858         menu->current = mutt_collapse_thread (Context, CURHDR);
1859         mutt_set_virtual (Context);
1860       }
1861       else {
1862         mutt_error (_("Thread contains unread messages."));
1863
1864         break;
1865       }
1866
1867       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1868
1869       break;
1870
1871     case OP_MAIN_COLLAPSE_ALL:
1872       CHECK_MSGCOUNT;
1873       CHECK_VISIBLE;
1874
1875       if ((Sort & SORT_MASK) != SORT_THREADS) {
1876         mutt_error (_("Threading is not enabled."));
1877
1878         break;
1879       }
1880
1881       {
1882         HEADER *h, *base;
1883         THREAD *thread, *top;
1884         int final;
1885
1886         if (CURHDR->collapsed)
1887           final = mutt_uncollapse_thread (Context, CURHDR);
1888         else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR))
1889           final = mutt_collapse_thread (Context, CURHDR);
1890         else
1891           final = CURHDR->virtual;
1892
1893         base = Context->hdrs[Context->v2r[final]];
1894
1895         top = Context->tree;
1896         Context->collapsed = !Context->collapsed;
1897         while ((thread = top) != NULL) {
1898           while (!thread->message)
1899             thread = thread->child;
1900           h = thread->message;
1901
1902           if (h->collapsed != Context->collapsed) {
1903             if (h->collapsed)
1904               mutt_uncollapse_thread (Context, h);
1905             else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (h))
1906               mutt_collapse_thread (Context, h);
1907           }
1908           top = top->next;
1909         }
1910
1911         mutt_set_virtual (Context);
1912         for (j = 0; j < Context->vcount; j++) {
1913           if (Context->hdrs[Context->v2r[j]]->index == base->index) {
1914             menu->current = j;
1915             break;
1916           }
1917         }
1918
1919         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1920       }
1921       break;
1922
1923       /* --------------------------------------------------------------------
1924        * These functions are invoked directly from the internal-pager
1925        */
1926
1927     case OP_BOUNCE_MESSAGE:
1928
1929       CHECK_ATTACH;
1930       CHECK_MSGCOUNT;
1931       CHECK_VISIBLE;
1932       ci_bounce_message (tag ? NULL : CURHDR, &menu->redraw);
1933       break;
1934
1935     case OP_CREATE_ALIAS:
1936
1937       mutt_create_alias (Context
1938                          && Context->vcount ? CURHDR->env : NULL, NULL);
1939       MAYBE_REDRAW (menu->redraw);
1940       menu->redraw |= REDRAW_CURRENT;
1941       break;
1942
1943     case OP_QUERY:
1944       CHECK_ATTACH;
1945       mutt_query_menu (NULL, 0);
1946       MAYBE_REDRAW (menu->redraw);
1947       break;
1948
1949     case OP_PURGE_MESSAGE:
1950     case OP_DELETE:
1951
1952       CHECK_MSGCOUNT;
1953       CHECK_VISIBLE;
1954       CHECK_READONLY;
1955
1956 #ifdef USE_IMAP
1957       CHECK_IMAP_ACL (IMAP_ACL_DELETE);
1958 #endif
1959
1960       if (tag) {
1961         mutt_tag_set_flag (M_DELETE, 1);
1962         mutt_tag_set_flag (M_PURGED, (op != OP_PURGE_MESSAGE) ? 0 : 1);
1963         if (option (OPTDELETEUNTAG))
1964           mutt_tag_set_flag (M_TAG, 0);
1965         menu->redraw = REDRAW_INDEX;
1966       }
1967       else {
1968         mutt_set_flag (Context, CURHDR, M_DELETE, 1);
1969         mutt_set_flag (Context, CURHDR, M_PURGED,
1970                        (op != OP_PURGE_MESSAGE) ? 0 : 1);
1971         if (option (OPTDELETEUNTAG))
1972           mutt_set_flag (Context, CURHDR, M_TAG, 0);
1973         if (option (OPTRESOLVE)) {
1974           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1975             menu->current = menu->oldcurrent;
1976             menu->redraw = REDRAW_CURRENT;
1977           }
1978           else if (menu->menu == MENU_PAGER) {
1979             op = OP_DISPLAY_MESSAGE;
1980             continue;
1981           }
1982           else
1983             menu->redraw |= REDRAW_MOTION_RESYNCH;
1984         }
1985         else
1986           menu->redraw = REDRAW_CURRENT;
1987       }
1988       menu->redraw |= REDRAW_STATUS;
1989       break;
1990
1991     case OP_DELETE_THREAD:
1992     case OP_DELETE_SUBTHREAD:
1993
1994       CHECK_MSGCOUNT;
1995       CHECK_VISIBLE;
1996       CHECK_READONLY;
1997
1998 #ifdef USE_IMAP
1999       CHECK_IMAP_ACL (IMAP_ACL_DELETE);
2000 #endif
2001
2002       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 1,
2003                                  op == OP_DELETE_THREAD ? 0 : 1);
2004
2005       if (rc != -1) {
2006         if (option (OPTDELETEUNTAG))
2007           mutt_thread_set_flag (CURHDR, M_TAG, 0,
2008                                 op == OP_DELETE_THREAD ? 0 : 1);
2009         if (option (OPTRESOLVE))
2010           if ((menu->current = ci_next_undeleted (menu->current)) == -1)
2011             menu->current = menu->oldcurrent;
2012         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2013       }
2014       break;
2015
2016 #ifdef USE_NNTP
2017     case OP_CATCHUP:
2018       if (Context && Context->magic == M_NNTP) {
2019         if (mutt_newsgroup_catchup (CurrentNewsSrv,
2020                                     ((NNTP_DATA *) Context->data)->group))
2021           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2022       }
2023       break;
2024 #endif
2025
2026     case OP_DISPLAY_ADDRESS:
2027
2028       CHECK_MSGCOUNT;
2029       CHECK_VISIBLE;
2030       mutt_display_address (CURHDR->env);
2031       break;
2032
2033     case OP_ENTER_COMMAND:
2034
2035       CurrentMenu = MENU_MAIN;
2036       mutt_enter_command ();
2037       mutt_check_rescore (Context);
2038       if (option (OPTFORCEREDRAWINDEX))
2039         menu->redraw = REDRAW_FULL;
2040       unset_option (OPTFORCEREDRAWINDEX);
2041       unset_option (OPTFORCEREDRAWPAGER);
2042       break;
2043
2044     case OP_EDIT_MESSAGE:
2045
2046       CHECK_MSGCOUNT;
2047       CHECK_VISIBLE;
2048       CHECK_READONLY;
2049       CHECK_ATTACH;
2050
2051 #ifdef USE_POP
2052       if (Context->magic == M_POP) {
2053         mutt_flushinp ();
2054         mutt_error (_("Can't edit message on POP server."));
2055
2056         break;
2057       }
2058 #endif
2059
2060 #ifdef USE_IMAP
2061       CHECK_IMAP_ACL (IMAP_ACL_INSERT);
2062 #endif
2063
2064 #ifdef USE_NNTP
2065       if (Context->magic == M_NNTP) {
2066         mutt_flushinp ();
2067         mutt_error (_("Can't edit message on newsserver."));
2068
2069         break;
2070       }
2071 #endif
2072
2073       if (option (OPTPGPAUTODEC)
2074           && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED)))
2075         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2076       mutt_edit_message (Context, tag ? NULL : CURHDR);
2077       menu->redraw = REDRAW_FULL;
2078
2079       break;
2080
2081     case OP_FORWARD_MESSAGE:
2082
2083       CHECK_MSGCOUNT;
2084       CHECK_VISIBLE;
2085       CHECK_ATTACH;
2086       ci_send_message (SENDFORWARD, NULL, NULL, Context, tag ? NULL : CURHDR);
2087       menu->redraw = REDRAW_FULL;
2088       break;
2089
2090
2091     case OP_FORGET_PASSPHRASE:
2092       crypt_forget_passphrase ();
2093       break;
2094
2095     case OP_GROUP_REPLY:
2096
2097       CHECK_MSGCOUNT;
2098       CHECK_VISIBLE;
2099       CHECK_ATTACH;
2100       ci_send_message (SENDREPLY | SENDGROUPREPLY, NULL, NULL, Context,
2101                        tag ? NULL : CURHDR);
2102       menu->redraw = REDRAW_FULL;
2103       break;
2104
2105     case OP_LIST_REPLY:
2106
2107       CHECK_ATTACH;
2108       CHECK_MSGCOUNT;
2109       CHECK_VISIBLE;
2110       ci_send_message (SENDREPLY | SENDLISTREPLY, NULL, NULL, Context,
2111                        tag ? NULL : CURHDR);
2112       menu->redraw = REDRAW_FULL;
2113       break;
2114
2115     case OP_MAIL:
2116
2117       CHECK_ATTACH;
2118       ci_send_message (0, NULL, NULL, Context, NULL);
2119       menu->redraw = REDRAW_FULL;
2120       break;
2121
2122     case OP_MAIL_KEY:
2123       if (!(WithCrypto & APPLICATION_PGP))
2124         break;
2125       CHECK_ATTACH;
2126       ci_send_message (SENDKEY, NULL, NULL, NULL, NULL);
2127       menu->redraw = REDRAW_FULL;
2128       break;
2129
2130
2131     case OP_EXTRACT_KEYS:
2132       if (!WithCrypto)
2133         break;
2134       CHECK_MSGCOUNT;
2135       CHECK_VISIBLE;
2136       crypt_extract_keys_from_messages (tag ? NULL : CURHDR);
2137       menu->redraw = REDRAW_FULL;
2138       break;
2139
2140
2141     case OP_CHECK_TRADITIONAL:
2142       if (!(WithCrypto & APPLICATION_PGP))
2143         break;
2144       CHECK_MSGCOUNT;
2145       CHECK_VISIBLE;
2146       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
2147         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2148
2149       if (menu->menu == MENU_PAGER) {
2150         op = OP_DISPLAY_MESSAGE;
2151         continue;
2152       }
2153       break;
2154
2155     case OP_PIPE:
2156
2157       CHECK_MSGCOUNT;
2158       CHECK_VISIBLE;
2159       mutt_pipe_message (tag ? NULL : CURHDR);
2160       MAYBE_REDRAW (menu->redraw);
2161       break;
2162
2163     case OP_PRINT:
2164
2165       CHECK_MSGCOUNT;
2166       CHECK_VISIBLE;
2167       mutt_print_message (tag ? NULL : CURHDR);
2168       break;
2169
2170     case OP_MAIN_READ_THREAD:
2171     case OP_MAIN_READ_SUBTHREAD:
2172
2173       CHECK_MSGCOUNT;
2174       CHECK_VISIBLE;
2175       CHECK_READONLY;
2176
2177 #ifdef USE_IMAP
2178       CHECK_IMAP_ACL (IMAP_ACL_SEEN);
2179 #endif
2180
2181       rc = mutt_thread_set_flag (CURHDR, M_READ, 1,
2182                                  op == OP_MAIN_READ_THREAD ? 0 : 1);
2183
2184       if (rc != -1) {
2185         if (option (OPTRESOLVE)) {
2186           if ((menu->current = (op == OP_MAIN_READ_THREAD ?
2187                                 mutt_next_thread (CURHDR) :
2188                                 mutt_next_subthread (CURHDR))) == -1)
2189             menu->current = menu->oldcurrent;
2190         }
2191         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2192       }
2193       break;
2194
2195     case OP_RECALL_MESSAGE:
2196
2197       CHECK_ATTACH;
2198       ci_send_message (SENDPOSTPONED, NULL, NULL, Context, NULL);
2199       menu->redraw = REDRAW_FULL;
2200       break;
2201
2202     case OP_RESEND:
2203
2204       CHECK_ATTACH;
2205       CHECK_MSGCOUNT;
2206       CHECK_VISIBLE;
2207
2208       if (tag) {
2209         for (j = 0; j < Context->vcount; j++) {
2210           if (Context->hdrs[Context->v2r[j]]->tagged)
2211             mutt_resend_message (NULL, Context,
2212                                  Context->hdrs[Context->v2r[j]]);
2213         }
2214       }
2215       else
2216         mutt_resend_message (NULL, Context, CURHDR);
2217
2218       menu->redraw = REDRAW_FULL;
2219       break;
2220
2221 #ifdef USE_NNTP
2222     case OP_POST:
2223     case OP_FOLLOWUP:
2224     case OP_FORWARD_TO_GROUP:
2225
2226       CHECK_ATTACH;
2227       if ((op == OP_FOLLOWUP || op == OP_FORWARD_TO_GROUP) &&
2228           Context && Context->msgcount == 0) {
2229         mutt_error (_("There are no messages."));
2230         sleep (2);
2231       }
2232       else if (op != OP_FOLLOWUP || !CURHDR->env->followup_to ||
2233                mutt_strcasecmp (CURHDR->env->followup_to, "poster") ||
2234                query_quadoption (OPT_FOLLOWUPTOPOSTER,
2235                                  _("Reply by mail as poster prefers?")) !=
2236                M_YES) {
2237         if (Context && Context->magic == M_NNTP
2238             && !((NNTP_DATA *) Context->data)->allowed
2239             && query_quadoption (OPT_TOMODERATED,
2240                                  _
2241                                  ("Posting to this group not allowed, may be moderated. Continue?"))
2242             != M_YES)
2243           break;
2244         if (op == OP_POST)
2245           ci_send_message (SENDNEWS, NULL, NULL, Context, NULL);
2246         else {
2247           CHECK_MSGCOUNT;
2248           if (op == OP_FOLLOWUP)
2249             ci_send_message (SENDNEWS | SENDREPLY, NULL, NULL, Context,
2250                              tag ? NULL : CURHDR);
2251           else
2252             ci_send_message (SENDNEWS | SENDFORWARD, NULL, NULL, Context,
2253                              tag ? NULL : CURHDR);
2254         }
2255         menu->redraw = REDRAW_FULL;
2256         break;
2257       }
2258 #endif
2259
2260     case OP_REPLY:
2261
2262       CHECK_ATTACH;
2263       CHECK_MSGCOUNT;
2264       CHECK_VISIBLE;
2265       ci_send_message (SENDREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
2266       menu->redraw = REDRAW_FULL;
2267       break;
2268
2269     case OP_SHELL_ESCAPE:
2270
2271       mutt_shell_escape ();
2272       MAYBE_REDRAW (menu->redraw);
2273       break;
2274
2275     case OP_TAG_THREAD:
2276     case OP_TAG_SUBTHREAD:
2277
2278       CHECK_MSGCOUNT;
2279       CHECK_VISIBLE;
2280       rc = mutt_thread_set_flag (CURHDR, M_TAG, !CURHDR->tagged,
2281                                  op == OP_TAG_THREAD ? 0 : 1);
2282
2283       if (rc != -1) {
2284         if (option (OPTRESOLVE)) {
2285           menu->current = mutt_next_thread (CURHDR);
2286
2287           if (menu->current == -1)
2288             menu->current = menu->oldcurrent;
2289         }
2290         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2291       }
2292       break;
2293
2294     case OP_UNDELETE:
2295
2296       CHECK_MSGCOUNT;
2297       CHECK_VISIBLE;
2298       CHECK_READONLY;
2299
2300 #ifdef USE_IMAP
2301       CHECK_IMAP_ACL (IMAP_ACL_DELETE);
2302 #endif
2303
2304       if (tag) {
2305         mutt_tag_set_flag (M_DELETE, 0);
2306         mutt_tag_set_flag (M_PURGED, 0);
2307         menu->redraw = REDRAW_INDEX;
2308       }
2309       else {
2310         mutt_set_flag (Context, CURHDR, M_DELETE, 0);
2311         mutt_set_flag (Context, CURHDR, M_PURGED, 0);
2312         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
2313           menu->current++;
2314           menu->redraw = REDRAW_MOTION_RESYNCH;
2315         }
2316         else
2317           menu->redraw = REDRAW_CURRENT;
2318       }
2319       menu->redraw |= REDRAW_STATUS;
2320       break;
2321
2322     case OP_UNDELETE_THREAD:
2323     case OP_UNDELETE_SUBTHREAD:
2324
2325       CHECK_MSGCOUNT;
2326       CHECK_VISIBLE;
2327       CHECK_READONLY;
2328
2329 #ifdef USE_IMAP
2330       CHECK_IMAP_ACL (IMAP_ACL_DELETE);
2331 #endif
2332
2333       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 0,
2334                                  op == OP_UNDELETE_THREAD ? 0 : 1)
2335         + mutt_thread_set_flag (CURHDR, M_PURGED, 0,
2336                                 op == OP_UNDELETE_THREAD ? 0 : 1);
2337
2338       if (rc > -1) {
2339         if (option (OPTRESOLVE)) {
2340           if (op == OP_UNDELETE_THREAD)
2341             menu->current = mutt_next_thread (CURHDR);
2342           else
2343             menu->current = mutt_next_subthread (CURHDR);
2344
2345           if (menu->current == -1)
2346             menu->current = menu->oldcurrent;
2347         }
2348         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2349       }
2350       break;
2351
2352     case OP_VERSION:
2353       mutt_version ();
2354       break;
2355
2356     case OP_BUFFY_LIST:
2357       mutt_buffy_list ();
2358       menu->redraw = REDRAW_FULL;
2359       break;
2360
2361     case OP_VIEW_ATTACHMENTS:
2362       CHECK_MSGCOUNT;
2363       CHECK_VISIBLE;
2364       mutt_view_attachments (CURHDR);
2365       if (CURHDR->attach_del)
2366         Context->changed = 1;
2367       menu->redraw = REDRAW_FULL;
2368       break;
2369
2370     case OP_END_COND:
2371       break;
2372
2373     case OP_WHAT_KEY:
2374       mutt_what_key ();
2375       break;
2376
2377     case OP_SIDEBAR_SCROLL_UP:
2378     case OP_SIDEBAR_SCROLL_DOWN:
2379     case OP_SIDEBAR_NEXT:
2380     case OP_SIDEBAR_PREV:
2381     case OP_SIDEBAR_NEXT_NEW:
2382     case OP_SIDEBAR_PREV_NEW:
2383       scroll_sidebar (op, menu->menu);
2384       break;
2385     default:
2386       if (menu->menu == MENU_MAIN)
2387         km_error_key (MENU_MAIN);
2388     }
2389
2390     if (menu->menu == MENU_PAGER) {
2391       menu->menu = MENU_MAIN;
2392       menu->redraw = REDRAW_FULL;
2393 #if 0
2394       set_option (OPTWEED);     /* turn header weeding back on. */
2395 #endif
2396     }
2397
2398     if (done)
2399       break;
2400   }
2401
2402 #ifdef USE_IMAP
2403   /* Close all open IMAP connections */
2404   if (!attach_msg)
2405     imap_logout_all ();
2406 #endif
2407 #ifdef USE_NNTP
2408   /* Close all open NNTP connections */
2409   if (!attach_msg)
2410     nntp_logout_all ();
2411 #endif
2412
2413   mutt_menuDestroy (&menu);
2414   return (close);
2415 }
2416
2417 void mutt_set_header_color (CONTEXT * ctx, HEADER * curhdr)
2418 {
2419   COLOR_LINE *color;
2420
2421   if (!curhdr)
2422     return;
2423
2424   for (color = ColorIndexList; color; color = color->next)
2425     if (mutt_pattern_exec
2426         (color->color_pattern, M_MATCH_FULL_ADDRESS, ctx, curhdr)) {
2427       curhdr->pair = color->pair;
2428       return;
2429     }
2430   curhdr->pair = ColorDefs[MT_COLOR_NORMAL];
2431 }