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