882ba357653f526eb0443c57a7485000dd9e446b
[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           draw_slidebar(menu);
596           menu->redraw |= REDRAW_STATUS;
597         }
598         else if (menu->redraw & (REDRAW_MOTION_RESYNCH | REDRAW_MOTION))
599           menu_redraw_motion (menu);
600         else if (menu->redraw & REDRAW_CURRENT)
601           menu_redraw_current (menu);
602       }
603
604       if (menu->redraw & REDRAW_STATUS) 
605       {
606         DrawFullLine = 1;
607         menu_status_line (buf, sizeof (buf), menu, NONULL (Status));
608         DrawFullLine = 0;
609         CLEARLINE (option (OPTSTATUSONTOP) ? 0 : LINES-2);
610         SETCOLOR (MT_COLOR_STATUS);
611         mutt_paddstr (COLS, buf);
612         SETCOLOR (MT_COLOR_NORMAL);
613         set_buffystats (Context);
614         menu->redraw &= ~REDRAW_STATUS;
615         if (option(OPTXTERMSETTITLES))
616         {
617           menu_status_line (buf, sizeof (buf), menu, NONULL (XtermTitle));
618           set_xterm_title_bar(buf);
619           menu_status_line (buf, sizeof (buf), menu, NONULL (XtermIcon));
620           set_xterm_icon_name(buf);
621         }
622       }
623
624       menu->redraw = 0;
625       if (menu->current < menu->max)
626         menu->oldcurrent = menu->current;
627       else
628         menu->oldcurrent = -1;
629
630       if (option (OPTARROWCURSOR))
631         move (menu->current - menu->top + menu->offset, 2);
632       else
633         move (menu->current - menu->top + menu->offset, COLS - 1);
634       mutt_refresh ();
635
636       op = km_dokey (MENU_MAIN);
637
638       dprint(4, (debugfile, "mutt_index_menu[%d]: Got op %d\n", __LINE__, op));
639
640 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
641       if (SigWinch)
642       {
643         mutt_flushinp ();
644         mutt_resize_screen ();
645         menu->redraw = REDRAW_FULL;
646         menu->menu = MENU_MAIN;
647         SigWinch = 0;
648         menu->top = 0; /* so we scroll the right amount */
649         /*
650          * force a real complete redraw.  clrtobot() doesn't seem to be able
651          * to handle every case without this.
652          */
653         clearok(stdscr,TRUE);
654         continue;
655       }
656 #endif
657
658       if (op == -1)
659         continue; /* either user abort or timeout */
660       
661       mutt_curs_set (1);
662       
663       /* special handling for the tag-prefix function */
664       if (op == OP_TAG_PREFIX)
665       {
666         if (!Context)
667         {
668           mutt_error _("No mailbox is open.");
669           continue;
670         }
671
672         if (!Context->tagged)
673         {
674           mutt_error _("No tagged messages.");
675           continue;
676         }
677         tag = 1;
678
679         /* give visual indication that the next command is a tag- command */
680         mvaddstr (LINES - 1, 0, "tag-");
681         clrtoeol ();
682
683         /* get the real command */
684         if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX)
685         {
686           /* abort tag sequence */
687           CLEARLINE (LINES-1);
688           continue;
689         }
690       }
691       else if (option (OPTAUTOTAG) && Context && Context->tagged)
692         tag = 1;
693
694       if (op == OP_TAG_PREFIX_COND)
695       {
696         if (!Context)
697         {
698           mutt_error _("No mailbox is open.");
699           continue;
700         }
701
702         if (!Context->tagged)
703         {
704           event_t tmp;
705           while(UngetCount>0)
706           {
707             tmp=mutt_getch();
708             if(tmp.op==OP_END_COND)break;
709           }
710           mutt_message  _("Nothing to do.");
711           continue;
712         }
713         tag = 1;
714
715         /* give visual indication that the next command is a tag- command */
716         mvaddstr (LINES - 1, 0, "tag-");
717         clrtoeol ();
718
719         /* get the real command */
720         if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX)
721         {
722           /* abort tag sequence */
723           CLEARLINE (LINES-1);
724           continue;
725         }
726       }
727
728       mutt_clear_error ();
729     }
730     else
731     {
732       if (menu->current < menu->max)
733         menu->oldcurrent = menu->current;
734       else
735         menu->oldcurrent = -1;
736       
737       mutt_curs_set (1);        /* fallback from the pager */
738     }
739
740 #ifdef USE_NNTP
741     unset_option (OPTNEWS);        /* for any case */
742 #endif
743
744     switch (op)
745     {
746
747       /* ----------------------------------------------------------------------
748        * movement commands
749        */
750
751       case OP_BOTTOM_PAGE:
752         menu_bottom_page (menu);
753         break;
754       case OP_FIRST_ENTRY:
755         menu_first_entry (menu);
756         break;
757       case OP_MIDDLE_PAGE:
758         menu_middle_page (menu);
759         break;
760       case OP_HALF_UP:
761         menu_half_up (menu);
762         break;
763       case OP_HALF_DOWN:
764         menu_half_down (menu);
765         break;
766       case OP_NEXT_LINE:
767         menu_next_line (menu);
768         break;
769       case OP_PREV_LINE:
770         menu_prev_line (menu);
771         break;
772       case OP_NEXT_PAGE:
773         menu_next_page (menu);
774         break;
775       case OP_PREV_PAGE:
776         menu_prev_page (menu);
777         break;
778       case OP_LAST_ENTRY:
779         menu_last_entry (menu);
780         break;
781       case OP_TOP_PAGE:
782         menu_top_page (menu);
783         break;
784       case OP_CURRENT_TOP:
785         menu_current_top (menu);
786         break;
787       case OP_CURRENT_MIDDLE:
788         menu_current_middle (menu);
789         break;
790       case OP_CURRENT_BOTTOM:
791         menu_current_bottom (menu);
792         break;
793
794 #ifdef USE_NNTP
795       case OP_GET_MESSAGE:
796       case OP_GET_PARENT:
797         CHECK_MSGCOUNT;
798         if (Context->magic == M_NNTP)
799         {
800           HEADER *h;
801
802           if (op == OP_GET_MESSAGE)
803           {
804             buf[0] = 0;
805             if (mutt_get_field (_("Enter Message-Id: "), buf, sizeof (buf), 0) != 0
806                   || !buf[0])
807               break;
808           }
809           else
810           {
811             LIST *ref = CURHDR->env->references;
812             if (!ref)
813             {
814               mutt_error _("Article has no parent reference!");
815               break;
816             }
817             strfcpy (buf, ref->data, sizeof (buf));
818           }
819           if (!Context->id_hash)
820             Context->id_hash = mutt_make_id_hash (Context);
821           if ((h = hash_find (Context->id_hash, buf)))
822           {
823             if (h->virtual != -1)
824             {
825               menu->current = h->virtual;
826               menu->redraw = REDRAW_MOTION_RESYNCH;
827             }
828             else if (h->collapsed)
829             {
830               mutt_uncollapse_thread (Context, h);
831               mutt_set_virtual (Context);
832               menu->current = h->virtual;
833               menu->redraw = REDRAW_MOTION_RESYNCH;
834             }
835             else
836               mutt_error _("Message not visible in limited view.");
837           }
838           else
839           {
840             if (nntp_check_msgid (Context, buf) == 0)
841             {
842               h = Context->hdrs[Context->msgcount-1];
843               mutt_sort_headers (Context, 0);
844               menu->current = h->virtual;
845               menu->redraw = REDRAW_FULL;
846             }
847             else
848               mutt_error (_("Article %s not found on server"), buf); 
849           }
850         }
851         break;
852
853       case OP_GET_CHILDREN:
854       case OP_RECONSTRUCT_THREAD:
855         CHECK_MSGCOUNT;
856         if (Context->magic == M_NNTP)
857         {
858           HEADER *h;
859           int old = CURHDR->index, i;
860
861           if (!CURHDR->env->message_id)
862           {
863             mutt_error _("No Message-Id. Unable to perform operation");
864             break;
865           }
866
867           if (!Context->id_hash)
868             Context->id_hash = mutt_make_id_hash (Context);
869           strfcpy (buf, CURHDR->env->message_id, sizeof (buf));
870
871           if (op == OP_RECONSTRUCT_THREAD)
872           {
873             LIST *ref = CURHDR->env->references;
874             while (ref)
875             {
876               nntp_check_msgid (Context, ref->data);
877               /* the last msgid in References is the root message */
878               if (!ref->next)
879                 strfcpy (buf, ref->data, sizeof (buf));
880               ref = ref->next;
881             }
882           }
883           mutt_message _("Check for children of message...");
884           if (nntp_check_children (Context, buf) == 0)
885           {
886             mutt_sort_headers (Context, (op == OP_RECONSTRUCT_THREAD));
887             h = hash_find (Context->id_hash, buf);
888             /* if the root message was retrieved, move to it */
889             if (h)
890               menu->current = h->virtual;
891             else /* try to restore old position */
892               for (i = 0; i < Context->msgcount; i++)
893                 if (Context->hdrs[i]->index == old)
894                 {
895                   menu->current = Context->hdrs[i]->virtual;
896                   /* As an added courtesy, recenter the menu
897                    * with the current entry at the middle of the screen */
898                   menu_check_recenter (menu);
899                   menu_current_middle (menu);
900                 }
901           }
902           menu->redraw = REDRAW_FULL;
903           mutt_clear_error ();
904         }
905         break;
906 #endif
907
908       case OP_JUMP:
909
910         CHECK_MSGCOUNT;
911         CHECK_VISIBLE;
912         if (isdigit (LastKey)) mutt_ungetch (LastKey, 0);
913         buf[0] = 0;
914         if (mutt_get_field (_("Jump to message: "), buf, sizeof (buf), 0) != 0
915             || !buf[0])
916           break;
917
918         if (! isdigit ((unsigned char) buf[0]))
919         {
920           mutt_error _("Argument must be a message number.");
921           break;
922         }
923
924         i = atoi (buf);
925         if (i > 0 && i <= Context->msgcount)
926         {
927           for (j = i-1; j < Context->msgcount; j++)
928           {
929             if (Context->hdrs[j]->virtual != -1)
930               break;
931           }
932           if (j >= Context->msgcount)
933           {
934             for (j = i-2; j >= 0; j--)
935             {
936               if (Context->hdrs[j]->virtual != -1)
937                 break;
938             }
939           }
940
941           if (j >= 0)
942           {
943             menu->current = Context->hdrs[j]->virtual;
944             if (menu->menu == MENU_PAGER)
945             {
946               op = OP_DISPLAY_MESSAGE;
947               continue;
948             }
949             else
950             menu->redraw = REDRAW_MOTION;
951           }
952           else
953             mutt_error _("That message is not visible.");
954         }
955         else
956           mutt_error _("Invalid message number.");
957
958         break;
959
960         /* --------------------------------------------------------------------
961          * `index' specific commands
962          */
963
964       case OP_MAIN_DELETE_PATTERN:
965
966         CHECK_MSGCOUNT;
967         CHECK_VISIBLE;
968         CHECK_READONLY;
969
970 #ifdef USE_IMAP
971 CHECK_IMAP_ACL(IMAP_ACL_DELETE);
972 #endif
973
974         CHECK_ATTACH;
975         mutt_pattern_func (M_DELETE, _("Delete messages matching: "));
976         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
977         break;
978
979 #ifdef USE_POP
980       case OP_MAIN_FETCH_MAIL:
981
982         CHECK_ATTACH;
983         pop_fetch_mail ();
984         menu->redraw = REDRAW_FULL;
985         break;
986 #endif /* USE_POP */
987
988       case OP_HELP:
989
990         mutt_help (MENU_MAIN);
991         menu->redraw = REDRAW_FULL;
992         break;
993
994       case OP_MAIN_SHOW_LIMIT:
995         CHECK_MSGCOUNT;
996         if (!Context->pattern)
997            mutt_message _("No limit pattern is in effect.");
998         else
999         {
1000            char buf[STRING];
1001            /* i18n: ask for a limit to apply */
1002            snprintf (buf, sizeof(buf), _("Limit: %s"),Context->pattern);
1003            mutt_message ("%s", buf);
1004         }
1005         break;
1006
1007       case OP_MAIN_LIMIT:
1008       case OP_TOGGLE_READ:
1009
1010         CHECK_MSGCOUNT;
1011         menu->oldcurrent = (Context->vcount && menu->current >= 0 && menu->current < Context->vcount) ?
1012                 CURHDR->index : -1;
1013         if (op == OP_TOGGLE_READ)
1014         {
1015           char buf[LONG_STRING];
1016
1017           if (!Context->pattern || strncmp (Context->pattern, "!~R!~D~s", 8) != 0)
1018           {
1019             snprintf (buf, sizeof (buf), "!~R!~D~s%s",
1020                       Context->pattern ? Context->pattern : ".*");
1021             set_option (OPTHIDEREAD);
1022           }
1023           else
1024           {
1025             strfcpy (buf, Context->pattern + 8, sizeof(buf));
1026             if (!*buf || strncmp (buf, ".*", 2) == 0)
1027               snprintf (buf, sizeof(buf), "~A");
1028             unset_option (OPTHIDEREAD);
1029           }
1030           FREE (&Context->pattern);
1031           Context->pattern = safe_strdup (buf);
1032         }
1033         if ((op == OP_TOGGLE_READ && mutt_pattern_func (M_LIMIT, NULL) == 0) ||
1034             mutt_pattern_func (M_LIMIT, _("Limit to messages matching: ")) == 0)
1035         {
1036           if (menu->oldcurrent >= 0)
1037           {
1038             /* try to find what used to be the current message */
1039             menu->current = -1;
1040             for (i = 0; i < Context->vcount; i++)
1041               if (Context->hdrs[Context->v2r[i]]->index == menu->oldcurrent)
1042               {
1043                 menu->current = i;
1044                 break;
1045               }
1046             if (menu->current < 0) menu->current = 0;
1047           }
1048           else
1049             menu->current = 0;
1050           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1051           if ((Sort & SORT_MASK) == SORT_THREADS)
1052             mutt_draw_tree (Context);
1053           menu->redraw = REDRAW_FULL;
1054         }
1055         break;          
1056
1057       case OP_QUIT:
1058
1059         close = op;
1060         if (attach_msg)
1061         {
1062          done = 1;
1063          break;
1064         }
1065
1066         if (query_quadoption (OPT_QUIT, _("Quit Mutt?")) == M_YES)
1067         {
1068           int check;
1069           
1070           oldcount = Context ? Context->msgcount : 0;
1071
1072           if (!Context || (check = mx_close_mailbox (Context, &index_hint)) == 0)
1073             done = 1;
1074           else
1075           {
1076             if (check == M_NEW_MAIL || check == M_REOPENED)
1077               update_index (menu, Context, check, oldcount, index_hint);
1078
1079             menu->redraw = REDRAW_FULL; /* new mail arrived? */
1080             set_option (OPTSEARCHINVALID);
1081           }
1082         }
1083         break;
1084
1085       case OP_REDRAW:
1086
1087         clearok (stdscr, TRUE);
1088         menu->redraw = REDRAW_FULL;
1089         break;
1090
1091       case OP_SEARCH:
1092       case OP_SEARCH_REVERSE:
1093       case OP_SEARCH_NEXT:
1094       case OP_SEARCH_OPPOSITE:
1095
1096         CHECK_MSGCOUNT;
1097         CHECK_VISIBLE;
1098         if ((menu->current = mutt_search_command (menu->current, op)) == -1)
1099           menu->current = menu->oldcurrent;
1100         else
1101           menu->redraw = REDRAW_MOTION;
1102         break;
1103
1104       case OP_SORT:
1105       case OP_SORT_REVERSE:
1106
1107         if (mutt_select_sort ((op == OP_SORT_REVERSE)) == 0)
1108         {
1109           if (Context && Context->msgcount)
1110           {
1111             resort_index (menu);
1112             set_option (OPTSEARCHINVALID);
1113           }
1114         }
1115         break;
1116
1117       case OP_TAG:
1118
1119         CHECK_MSGCOUNT;
1120         CHECK_VISIBLE;
1121         if (tag && !option (OPTAUTOTAG))
1122         {
1123           for (j = 0; j < Context->vcount; j++)
1124             mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_TAG, 0);
1125           menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1126         }
1127         else
1128         {
1129           mutt_set_flag (Context, CURHDR, M_TAG, !CURHDR->tagged);
1130           Context->last_tag = CURHDR->tagged ? CURHDR :
1131             ((Context->last_tag == CURHDR && !CURHDR->tagged)
1132             ? NULL : Context->last_tag);
1133           menu->redraw = REDRAW_STATUS;
1134           if (option (OPTRESOLVE) && menu->current < Context->vcount - 1)
1135           {
1136             menu->current++;
1137             menu->redraw |= REDRAW_MOTION_RESYNCH;
1138           }
1139           else
1140             menu->redraw |= REDRAW_CURRENT;
1141         }
1142         break;
1143
1144       case OP_MAIN_TAG_PATTERN:
1145
1146         CHECK_MSGCOUNT;
1147         CHECK_VISIBLE;
1148         mutt_pattern_func (M_TAG, _("Tag messages matching: "));
1149         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1150         break;
1151
1152       case OP_MAIN_UNDELETE_PATTERN:
1153
1154         CHECK_MSGCOUNT;
1155         CHECK_VISIBLE;
1156         CHECK_READONLY;
1157
1158 #ifdef USE_IMAP
1159 CHECK_IMAP_ACL(IMAP_ACL_DELETE);
1160 #endif
1161
1162         if (mutt_pattern_func (M_UNDELETE, _("Undelete messages matching: ")) == 0)
1163           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1164         break;
1165
1166       case OP_MAIN_UNTAG_PATTERN:
1167
1168         CHECK_MSGCOUNT;
1169         CHECK_VISIBLE;
1170         if (mutt_pattern_func (M_UNTAG, _("Untag messages matching: ")) == 0)
1171           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1172         break;
1173
1174         /* --------------------------------------------------------------------
1175          * The following operations can be performed inside of the pager.
1176          */
1177
1178 #ifdef USE_IMAP
1179       case OP_MAIN_IMAP_FETCH:
1180         if (Context->magic == M_IMAP)
1181           imap_check_mailbox (Context, &index_hint, 1);
1182         break;
1183 #endif
1184       
1185       case OP_MAIN_SYNC_FOLDER:
1186
1187         if (Context && !Context->msgcount)
1188           break;
1189
1190         CHECK_MSGCOUNT;
1191         CHECK_VISIBLE;
1192         CHECK_READONLY;
1193         {
1194           int oldvcount = Context->vcount;
1195           int oldcount  = Context->msgcount;
1196           int dcount = 0;
1197           int check;
1198
1199           /* calculate the number of messages _above_ the cursor,
1200            * so we can keep the cursor on the current message
1201            */ 
1202           for (j = 0; j <= menu->current; j++)
1203           {
1204             if (Context->hdrs[Context->v2r[j]]->deleted)
1205               dcount++;
1206           }
1207
1208           if ((check = mx_sync_mailbox (Context, &index_hint)) == 0)
1209           {
1210             if (Context->vcount != oldvcount)
1211               menu->current -= dcount;
1212             set_option (OPTSEARCHINVALID);
1213           }
1214           else if (check == M_NEW_MAIL || check == M_REOPENED)
1215             update_index (menu, Context, check, oldcount, index_hint);
1216
1217           /* 
1218            * do a sanity check even if mx_sync_mailbox failed.
1219            */
1220
1221           if (menu->current < 0 || menu->current >= Context->vcount)
1222             menu->current = ci_first_message ();
1223         }
1224
1225         /* check for a fatal error, or all messages deleted */
1226         if (!Context->path)
1227           FREE (&Context);
1228
1229         /* if we were in the pager, redisplay the message */
1230         if (menu->menu == MENU_PAGER)
1231         {
1232           op = OP_DISPLAY_MESSAGE;
1233           continue;
1234         }
1235         else
1236           menu->redraw = REDRAW_FULL;
1237         break;
1238
1239       case OP_SIDEBAR_OPEN:
1240       case OP_MAIN_CHANGE_FOLDER:
1241       case OP_MAIN_CHANGE_FOLDER_READONLY:
1242 #ifdef USE_NNTP
1243       case OP_MAIN_CHANGE_GROUP:
1244       case OP_MAIN_CHANGE_GROUP_READONLY:
1245 #endif
1246         if (attach_msg || option (OPTREADONLY) ||
1247 #ifdef USE_NNTP
1248             op == OP_MAIN_CHANGE_GROUP_READONLY ||
1249 #endif
1250             op == OP_MAIN_CHANGE_FOLDER_READONLY)
1251           flags = M_READONLY;
1252         else
1253           flags = 0;
1254
1255         if (flags)
1256           cp = _("Open mailbox in read-only mode");
1257         else
1258           cp = _("Open mailbox");
1259
1260         buf[0] = '\0';
1261 #ifdef USE_NNTP
1262         unset_option (OPTNEWS);
1263         if (op == OP_MAIN_CHANGE_GROUP ||
1264             op == OP_MAIN_CHANGE_GROUP_READONLY)
1265         {
1266           set_option (OPTNEWS);
1267           if (!(CurrentNewsSrv = mutt_select_newsserver (NewsServer)))
1268             break;
1269           if (flags)
1270             cp = _("Open newsgroup in read-only mode");
1271           else
1272             cp = _("Open newsgroup");
1273           nntp_buffy (buf);
1274         }
1275         else
1276 #endif
1277         mutt_buffy (buf, sizeof (buf));
1278
1279         if ( op == OP_SIDEBAR_OPEN ) {
1280           if(!CurBuffy)
1281             break;
1282           strncpy( buf, CurBuffy->path, sizeof(buf) );
1283         } else if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1)
1284           break;
1285         if (!buf[0])
1286         {
1287           CLEARLINE (LINES-1);
1288           break;
1289         }
1290
1291 #ifdef USE_NNTP
1292         if (option (OPTNEWS))
1293         {
1294           unset_option (OPTNEWS);
1295           nntp_expand_path (buf, sizeof (buf), &CurrentNewsSrv->conn->account);
1296         }
1297         else
1298 #endif
1299         mutt_expand_path (buf, sizeof (buf));
1300         set_curbuffy(buf);
1301         if (mx_get_magic (buf) <= 0)
1302         {
1303           mutt_error (_("%s is not a mailbox."), buf);
1304           break;
1305         }
1306
1307         if (Context)
1308         {
1309           int check;
1310
1311 #ifdef USE_COMPRESSED
1312           if (Context->compressinfo && Context->realpath)
1313             mutt_str_replace (&LastFolder, Context->realpath);
1314           else
1315 #endif
1316
1317           mutt_str_replace (&LastFolder, Context->path);
1318           oldcount = Context ? Context->msgcount : 0;
1319
1320           if ((check = mx_close_mailbox (Context, &index_hint)) != 0)
1321           {
1322             if (check == M_NEW_MAIL || check == M_REOPENED)
1323               update_index (menu, Context, check, oldcount, index_hint);
1324                 
1325             set_option (OPTSEARCHINVALID);
1326             menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1327             break;
1328           }
1329           FREE (&Context);
1330         }
1331
1332         mutt_sleep (0);
1333       
1334         /* Set CurrentMenu to MENU_MAIN before executing any folder
1335          * hooks so that all the index menu functions are available to
1336          * the exec command.
1337          */
1338
1339         CurrentMenu = MENU_MAIN;
1340         mutt_folder_hook (buf);
1341
1342         if ((Context = mx_open_mailbox (buf, flags, NULL)) != NULL)
1343         {
1344           menu->current = ci_first_message ();
1345         }
1346         else
1347           menu->current = 0;
1348
1349 #ifdef USE_NNTP
1350         /* mutt_buffy_check() must be done with mail-reader mode! */
1351         menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN,
1352           (Context && (Context->magic == M_NNTP)) ? IndexNewsHelp : IndexHelp);
1353 #endif
1354         mutt_clear_error ();
1355         mutt_buffy_check(1); /* force the buffy check after we have changed
1356                               the folder */
1357         menu->redraw = REDRAW_FULL;
1358         set_option (OPTSEARCHINVALID);
1359         break;
1360
1361       case OP_DISPLAY_MESSAGE:
1362       case OP_DISPLAY_HEADERS: /* don't weed the headers */
1363
1364         CHECK_MSGCOUNT;
1365         CHECK_VISIBLE;
1366         /*
1367          * toggle the weeding of headers so that a user can press the key
1368          * again while reading the message.
1369          */
1370         if (op == OP_DISPLAY_HEADERS)
1371           toggle_option (OPTWEED);
1372
1373         unset_option (OPTNEEDRESORT);
1374
1375         if ((Sort & SORT_MASK) == SORT_THREADS && CURHDR->collapsed)
1376         {
1377           mutt_uncollapse_thread (Context, CURHDR);
1378           mutt_set_virtual (Context);
1379           if (option (OPTUNCOLLAPSEJUMP))
1380             menu->current = mutt_thread_next_unread (Context, CURHDR);
1381         }
1382  
1383         if ((op = mutt_display_message (CURHDR)) == -1)
1384         {
1385           unset_option (OPTNEEDRESORT);
1386           break;
1387         }
1388
1389         menu->menu = MENU_PAGER;
1390          menu->oldcurrent = menu->current;
1391         continue;
1392
1393       case OP_EXIT:
1394
1395         close = op;
1396         if (menu->menu == MENU_MAIN && attach_msg)
1397         {
1398          done = 1;
1399          break;
1400         }
1401
1402         if ((menu->menu == MENU_MAIN)
1403             && (query_quadoption (OPT_QUIT, 
1404                                   _("Exit Mutt-ng without saving?")) == M_YES))
1405         {
1406           if (Context)
1407           {
1408             mx_fastclose_mailbox (Context);
1409             FREE (&Context);
1410           }
1411           done = 1;
1412         }
1413         break;
1414
1415       case OP_EDIT_TYPE:
1416
1417         CHECK_MSGCOUNT;
1418         CHECK_VISIBLE;
1419         CHECK_ATTACH;
1420         mutt_edit_content_type (CURHDR, CURHDR->content, NULL);
1421         /* if we were in the pager, redisplay the message */
1422         if (menu->menu == MENU_PAGER)
1423         {
1424           op = OP_DISPLAY_MESSAGE;
1425           continue;
1426         }
1427         else
1428           menu->redraw = REDRAW_CURRENT;
1429         break;
1430
1431       case OP_MAIN_NEXT_UNDELETED:
1432
1433         CHECK_MSGCOUNT;
1434         CHECK_VISIBLE;
1435         if (menu->current >= Context->vcount - 1)
1436         {
1437           if (menu->menu == MENU_MAIN)
1438             mutt_error _("You are on the last message.");
1439           break;
1440         }
1441         if ((menu->current = ci_next_undeleted (menu->current)) == -1)
1442         {
1443           menu->current = menu->oldcurrent;
1444           if (menu->menu == MENU_MAIN)
1445             mutt_error _("No undeleted messages.");
1446         }
1447         else if (menu->menu == MENU_PAGER)
1448         {
1449           op = OP_DISPLAY_MESSAGE;
1450           continue;
1451         }
1452         else
1453           menu->redraw = REDRAW_MOTION;
1454         break;
1455
1456       case OP_NEXT_ENTRY:
1457
1458         CHECK_MSGCOUNT;
1459         CHECK_VISIBLE;
1460         if (menu->current >= Context->vcount - 1)
1461         {
1462           if (menu->menu == MENU_MAIN)
1463             mutt_error _("You are on the last message.");
1464           break;
1465         }
1466         menu->current++;
1467         if (menu->menu == MENU_PAGER)
1468         {
1469           op = OP_DISPLAY_MESSAGE;
1470           continue;
1471         }
1472         else
1473           menu->redraw = REDRAW_MOTION;
1474         break;
1475
1476       case OP_MAIN_PREV_UNDELETED:
1477
1478         CHECK_MSGCOUNT;
1479         CHECK_VISIBLE;
1480         if (menu->current < 1)
1481         {
1482           mutt_error _("You are on the first message.");
1483           break;
1484         }
1485         if ((menu->current = ci_previous_undeleted (menu->current)) == -1)
1486         {
1487           menu->current = menu->oldcurrent;
1488           if (menu->menu == MENU_MAIN)
1489             mutt_error _("No undeleted messages.");
1490         }
1491         else if (menu->menu == MENU_PAGER)
1492         {
1493           op = OP_DISPLAY_MESSAGE;
1494           continue;
1495         }
1496         else
1497           menu->redraw = REDRAW_MOTION;
1498         break;
1499
1500       case OP_PREV_ENTRY:
1501
1502         CHECK_MSGCOUNT;
1503         CHECK_VISIBLE;
1504         if (menu->current < 1)
1505         {
1506           if (menu->menu == MENU_MAIN) mutt_error _("You are on the first message.");
1507           break;
1508         }
1509         menu->current--;
1510         if (menu->menu == MENU_PAGER)
1511         {
1512           op = OP_DISPLAY_MESSAGE;
1513           continue;
1514         }
1515         else
1516           menu->redraw = REDRAW_MOTION;
1517         break;
1518
1519       case OP_DECRYPT_COPY:
1520       case OP_DECRYPT_SAVE:
1521         if (!WithCrypto)
1522           break;   
1523         /* fall thru */
1524       case OP_COPY_MESSAGE:
1525       case OP_SAVE:
1526       case OP_DECODE_COPY:
1527       case OP_DECODE_SAVE:
1528         CHECK_MSGCOUNT;
1529         CHECK_VISIBLE;
1530         if (mutt_save_message (tag ? NULL : CURHDR,
1531                                (op == OP_DECRYPT_SAVE) ||
1532                                (op == OP_SAVE) || (op == OP_DECODE_SAVE),
1533                                (op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY),
1534                                (op == OP_DECRYPT_SAVE) || (op == OP_DECRYPT_COPY) ||
1535                                0,
1536                                &menu->redraw) == 0 &&
1537              (op == OP_SAVE || op == OP_DECODE_SAVE || op == OP_DECRYPT_SAVE)
1538             )
1539         {
1540           if (tag)
1541             menu->redraw |= REDRAW_INDEX;
1542           else if (option (OPTRESOLVE))
1543           {
1544             if ((menu->current = ci_next_undeleted (menu->current)) == -1)
1545             {
1546               menu->current = menu->oldcurrent;
1547               menu->redraw |= REDRAW_CURRENT;
1548             }
1549             else
1550               menu->redraw |= REDRAW_MOTION_RESYNCH;
1551           }
1552           else
1553             menu->redraw |= REDRAW_CURRENT;
1554         }
1555         break;
1556
1557       case OP_MAIN_NEXT_NEW:
1558       case OP_MAIN_NEXT_UNREAD:
1559       case OP_MAIN_PREV_NEW:
1560       case OP_MAIN_PREV_UNREAD:
1561       case OP_MAIN_NEXT_NEW_THEN_UNREAD:
1562       case OP_MAIN_PREV_NEW_THEN_UNREAD:
1563
1564       {
1565         int first_unread = -1;
1566         int first_new    = -1;
1567         
1568         CHECK_MSGCOUNT;
1569         CHECK_VISIBLE;
1570
1571         i = menu->current;
1572         menu->current = -1;
1573         for (j = 0; j != Context->vcount; j++)
1574         {
1575 #define CURHDRi Context->hdrs[Context->v2r[i]] 
1576           if (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_NEXT_NEW_THEN_UNREAD)
1577           {
1578             i++;
1579             if (i > Context->vcount - 1)
1580             {
1581               mutt_message _("Search wrapped to top.");
1582               i = 0;
1583             }
1584           }
1585           else
1586           {
1587             i--;
1588             if (i < 0)
1589             {
1590               mutt_message _("Search wrapped to bottom.");
1591               i = Context->vcount - 1;
1592             }
1593           }
1594
1595           if (CURHDRi->collapsed && (Sort & SORT_MASK) == SORT_THREADS)
1596           {
1597             if (UNREAD (CURHDRi) && first_unread == -1)
1598               first_unread = i;
1599             if (UNREAD (CURHDRi) == 1 && first_new == -1)
1600               first_new = i;
1601           }
1602           else if ((!CURHDRi->deleted && !CURHDRi->read))
1603           {
1604             if (first_unread == -1)
1605               first_unread = i;
1606             if ((!CURHDRi->old) && first_new == -1)
1607               first_new = i;
1608           }
1609           
1610           if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD) &&
1611               first_unread != -1)
1612             break;
1613           if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1614                op == OP_MAIN_NEXT_NEW_THEN_UNREAD || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1615               && first_new != -1)
1616             break;
1617         }
1618 #undef CURHDRi
1619         if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1620              op == OP_MAIN_NEXT_NEW_THEN_UNREAD || op == OP_MAIN_PREV_NEW_THEN_UNREAD) 
1621             && first_new != -1)
1622           menu->current = first_new;
1623         else if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD ||
1624                   op == OP_MAIN_NEXT_NEW_THEN_UNREAD || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1625                  && first_unread != -1)
1626           menu->current = first_unread;
1627
1628         if (menu->current == -1)
1629         {
1630           menu->current = menu->oldcurrent;
1631           mutt_error ("%s%s.", (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW) ? _("No new messages") : _("No unread messages"),
1632                       Context->pattern ? _(" in this limited view") : "");
1633         }
1634         else if (menu->menu == MENU_PAGER)
1635         {
1636           op = OP_DISPLAY_MESSAGE;
1637           continue;
1638         }
1639         else
1640           menu->redraw = REDRAW_MOTION;
1641         break;
1642       }
1643       case OP_FLAG_MESSAGE:
1644
1645         CHECK_MSGCOUNT;
1646         CHECK_VISIBLE;
1647         CHECK_READONLY;
1648
1649 #ifdef USE_POP
1650         if (Context->magic == M_POP)
1651         {
1652           mutt_flushinp ();
1653           mutt_error _("Can't change 'important' flag on POP server.");
1654           break;
1655         }
1656 #endif
1657
1658 #ifdef USE_IMAP
1659 CHECK_IMAP_ACL(IMAP_ACL_WRITE);
1660 #endif
1661
1662 #ifdef USE_NNTP
1663         if (Context->magic == M_NNTP)
1664         {
1665           mutt_flushinp ();
1666           mutt_error _("Can't change 'important' flag on NNTP server.");
1667           break;
1668         }
1669 #endif
1670
1671         if (tag)
1672         {
1673           for (j = 0; j < Context->vcount; j++)
1674           {
1675             if (Context->hdrs[Context->v2r[j]]->tagged)
1676               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]],
1677                              M_FLAG, !Context->hdrs[Context->v2r[j]]->flagged);
1678           }
1679
1680           menu->redraw |= REDRAW_INDEX;
1681         }
1682         else
1683         {
1684           mutt_set_flag (Context, CURHDR, M_FLAG, !CURHDR->flagged);
1685           if (option (OPTRESOLVE))
1686           {
1687             if ((menu->current = ci_next_undeleted (menu->current)) == -1)
1688             {
1689               menu->current = menu->oldcurrent;
1690               menu->redraw = REDRAW_CURRENT;
1691             }
1692             else
1693               menu->redraw = REDRAW_MOTION_RESYNCH;
1694           }
1695           else
1696             menu->redraw = REDRAW_CURRENT;
1697         }
1698         menu->redraw |= REDRAW_STATUS;
1699         break;
1700
1701       case OP_TOGGLE_NEW:
1702
1703         CHECK_MSGCOUNT;
1704         CHECK_VISIBLE;
1705         CHECK_READONLY;
1706
1707 #ifdef USE_IMAP
1708 CHECK_IMAP_ACL(IMAP_ACL_SEEN);
1709 #endif
1710
1711         if (tag)
1712         {
1713           for (j = 0; j < Context->vcount; j++)
1714           {
1715             if (Context->hdrs[Context->v2r[j]]->tagged)
1716             {
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, 1);
1720               else
1721                 mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_READ, 1);
1722             }
1723           }
1724           menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1725         }
1726         else
1727         {
1728           if (CURHDR->read || CURHDR->old)
1729             mutt_set_flag (Context, CURHDR, M_NEW, 1);
1730           else
1731             mutt_set_flag (Context, CURHDR, M_READ, 1);
1732
1733           if (option (OPTRESOLVE))
1734           {
1735             if ((menu->current = ci_next_undeleted (menu->current)) == -1)
1736             {
1737               menu->current = menu->oldcurrent;
1738               menu->redraw = REDRAW_CURRENT;
1739             }
1740             else
1741               menu->redraw = REDRAW_MOTION_RESYNCH;
1742           }
1743           else
1744             menu->redraw = REDRAW_CURRENT;
1745           menu->redraw |= REDRAW_STATUS;
1746         }
1747         break;
1748
1749       case OP_TOGGLE_WRITE:
1750
1751         CHECK_MSGCOUNT;
1752         if (mx_toggle_write (Context) == 0)
1753           menu->redraw |= REDRAW_STATUS;
1754         break;
1755
1756       case OP_MAIN_NEXT_THREAD:
1757       case OP_MAIN_NEXT_SUBTHREAD:
1758       case OP_MAIN_PREV_THREAD:
1759       case OP_MAIN_PREV_SUBTHREAD:
1760
1761         CHECK_MSGCOUNT;
1762         CHECK_VISIBLE;
1763         switch (op)
1764         {
1765           case OP_MAIN_NEXT_THREAD:
1766             menu->current = mutt_next_thread (CURHDR);
1767             break;
1768
1769           case OP_MAIN_NEXT_SUBTHREAD:
1770             menu->current = mutt_next_subthread (CURHDR);
1771             break;
1772             
1773           case OP_MAIN_PREV_THREAD:
1774             menu->current = mutt_previous_thread (CURHDR);
1775             break;
1776
1777           case OP_MAIN_PREV_SUBTHREAD:
1778             menu->current = mutt_previous_subthread (CURHDR);
1779             break;
1780         }
1781
1782         if (menu->current < 0)
1783         {
1784           menu->current = menu->oldcurrent;
1785           if (op == OP_MAIN_NEXT_THREAD || op == OP_MAIN_NEXT_SUBTHREAD)
1786             mutt_error _("No more threads.");
1787           else
1788             mutt_error _("You are on the first thread.");
1789         }
1790         else if (menu->menu == MENU_PAGER)
1791         {
1792           op = OP_DISPLAY_MESSAGE;
1793           continue;
1794         }
1795         else
1796           menu->redraw = REDRAW_MOTION;
1797         break;
1798
1799       case OP_MAIN_PARENT_MESSAGE:
1800
1801         CHECK_MSGCOUNT;
1802         CHECK_VISIBLE;
1803
1804         if ((menu->current = mutt_parent_message (Context, CURHDR)) < 0)
1805         {
1806           menu->current = menu->oldcurrent;
1807         }
1808         else if (menu->menu == MENU_PAGER)
1809         {
1810           op = OP_DISPLAY_MESSAGE;
1811           continue;
1812         }
1813         else
1814           menu->redraw = REDRAW_MOTION;
1815         break;
1816
1817       case OP_MAIN_SET_FLAG:
1818       case OP_MAIN_CLEAR_FLAG:
1819
1820         CHECK_MSGCOUNT;
1821         CHECK_VISIBLE;
1822         CHECK_READONLY;
1823         
1824 /* #ifdef USE_IMAP
1825 CHECK_IMAP_ACL(IMAP_ACL_WRITE);
1826 #endif */
1827
1828         if (mutt_change_flag (tag ? NULL : CURHDR, (op == OP_MAIN_SET_FLAG)) == 0)
1829         {
1830           menu->redraw = REDRAW_STATUS;
1831           if (tag)
1832             menu->redraw |= REDRAW_INDEX;
1833           else if (option (OPTRESOLVE))
1834           {
1835             if ((menu->current = ci_next_undeleted (menu->current)) == -1)
1836             {
1837               menu->current = menu->oldcurrent;
1838               menu->redraw |= REDRAW_CURRENT;
1839             }
1840             else
1841               menu->redraw |= REDRAW_MOTION_RESYNCH;
1842           }
1843           else
1844             menu->redraw |= REDRAW_CURRENT;
1845         }
1846         break;
1847
1848       case OP_MAIN_COLLAPSE_THREAD:
1849         CHECK_MSGCOUNT;
1850         CHECK_VISIBLE;
1851
1852         if ((Sort & SORT_MASK) != SORT_THREADS)
1853         {
1854           mutt_error _("Threading is not enabled.");
1855           break;
1856         }
1857       
1858         if (CURHDR->collapsed)
1859         {
1860           menu->current = mutt_uncollapse_thread (Context, CURHDR);
1861           mutt_set_virtual (Context);
1862           if (option (OPTUNCOLLAPSEJUMP))
1863             menu->current = mutt_thread_next_unread (Context, CURHDR);
1864         }
1865         else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR))
1866         {
1867           menu->current = mutt_collapse_thread (Context, CURHDR);
1868           mutt_set_virtual (Context);
1869         }
1870         else
1871         {
1872           mutt_error _("Thread contains unread messages.");
1873           break;
1874         }
1875
1876         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1877
1878        break;
1879
1880       case OP_MAIN_COLLAPSE_ALL:
1881         CHECK_MSGCOUNT;
1882         CHECK_VISIBLE;
1883
1884         if ((Sort & SORT_MASK) != SORT_THREADS)
1885         {
1886           mutt_error _("Threading is not enabled.");
1887           break;
1888         }
1889
1890         {
1891           HEADER *h, *base;
1892           THREAD *thread, *top;
1893           int final;
1894           
1895           if (CURHDR->collapsed)
1896             final = mutt_uncollapse_thread (Context, CURHDR);
1897           else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR))
1898             final = mutt_collapse_thread (Context, CURHDR);
1899           else
1900             final = CURHDR->virtual;
1901           
1902           base = Context->hdrs[Context->v2r[final]];
1903           
1904           top = Context->tree;
1905           Context->collapsed = !Context->collapsed;
1906           while ((thread = top) != NULL)
1907           {
1908             while (!thread->message)
1909               thread = thread->child;
1910             h = thread->message;
1911
1912             if (h->collapsed != Context->collapsed)
1913             {
1914               if (h->collapsed)
1915                 mutt_uncollapse_thread (Context, h);
1916               else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (h))
1917                 mutt_collapse_thread (Context, h);
1918             }
1919             top = top->next;
1920           }
1921           
1922           mutt_set_virtual (Context);
1923           for (j = 0; j < Context->vcount; j++)
1924           {
1925             if (Context->hdrs[Context->v2r[j]]->index == base->index)
1926             {
1927               menu->current = j;
1928               break;
1929             }
1930           }
1931           
1932           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1933         }
1934         break;
1935       
1936       /* --------------------------------------------------------------------
1937        * These functions are invoked directly from the internal-pager
1938        */
1939
1940       case OP_BOUNCE_MESSAGE:
1941
1942         CHECK_ATTACH;
1943         CHECK_MSGCOUNT;
1944         CHECK_VISIBLE;
1945         ci_bounce_message (tag ? NULL : CURHDR, &menu->redraw);
1946         break;
1947
1948       case OP_CREATE_ALIAS:
1949
1950         mutt_create_alias (Context && Context->vcount ? CURHDR->env : NULL, NULL);
1951         MAYBE_REDRAW (menu->redraw);
1952         menu->redraw |= REDRAW_CURRENT;
1953         break;
1954
1955       case OP_QUERY:
1956         CHECK_ATTACH;
1957         mutt_query_menu (NULL, 0);
1958         MAYBE_REDRAW (menu->redraw);
1959         break;
1960
1961       case OP_PURGE_MESSAGE:
1962       case OP_DELETE:
1963
1964         CHECK_MSGCOUNT;
1965         CHECK_VISIBLE;
1966         CHECK_READONLY;
1967         
1968 #ifdef USE_IMAP
1969 CHECK_IMAP_ACL(IMAP_ACL_DELETE);
1970 #endif
1971
1972         if (tag)
1973         {
1974           mutt_tag_set_flag (M_DELETE, 1);
1975           mutt_tag_set_flag (M_PURGED, (op != OP_PURGE_MESSAGE) ? 0 : 1);
1976           if (option (OPTDELETEUNTAG))
1977             mutt_tag_set_flag (M_TAG, 0);
1978           menu->redraw = REDRAW_INDEX;
1979         }
1980         else
1981         {
1982           mutt_set_flag (Context, CURHDR, M_DELETE, 1);
1983           mutt_set_flag (Context, CURHDR, M_PURGED,
1984                          (op != OP_PURGE_MESSAGE) ? 0 : 1);
1985           if (option (OPTDELETEUNTAG))
1986             mutt_set_flag (Context, CURHDR, M_TAG, 0);
1987           if (option (OPTRESOLVE))
1988           {
1989             if ((menu->current = ci_next_undeleted (menu->current)) == -1)
1990             {
1991               menu->current = menu->oldcurrent;
1992               menu->redraw = REDRAW_CURRENT;
1993             }
1994             else if (menu->menu == MENU_PAGER)
1995             {
1996               op = OP_DISPLAY_MESSAGE;
1997               continue;
1998             }
1999             else
2000               menu->redraw |= REDRAW_MOTION_RESYNCH;
2001           }
2002           else
2003             menu->redraw = REDRAW_CURRENT;
2004         }
2005         menu->redraw |= REDRAW_STATUS;
2006         break;
2007
2008       case OP_DELETE_THREAD:
2009       case OP_DELETE_SUBTHREAD:
2010
2011         CHECK_MSGCOUNT;
2012         CHECK_VISIBLE;
2013         CHECK_READONLY;
2014
2015 #ifdef USE_IMAP
2016 CHECK_IMAP_ACL(IMAP_ACL_DELETE);
2017 #endif
2018
2019         rc = mutt_thread_set_flag (CURHDR, M_DELETE, 1,
2020                                    op == OP_DELETE_THREAD ? 0 : 1);
2021
2022         if (rc != -1)
2023         {
2024           if (option (OPTDELETEUNTAG))
2025             mutt_thread_set_flag (CURHDR, M_TAG, 0, 
2026                                   op == OP_DELETE_THREAD ? 0 : 1);
2027           if (option (OPTRESOLVE))
2028             if ((menu->current = ci_next_undeleted (menu->current)) == -1)
2029               menu->current = menu->oldcurrent;
2030           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2031         }
2032         break;
2033
2034 #ifdef USE_NNTP
2035       case OP_CATCHUP:
2036         if (Context && Context->magic == M_NNTP)
2037         {
2038           if (mutt_newsgroup_catchup (CurrentNewsSrv,
2039                 ((NNTP_DATA *)Context->data)->group))
2040             menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2041         }
2042         break;
2043 #endif
2044
2045       case OP_DISPLAY_ADDRESS:
2046
2047         CHECK_MSGCOUNT;
2048         CHECK_VISIBLE;
2049         mutt_display_address (CURHDR->env);
2050         break;
2051
2052       case OP_ENTER_COMMAND:
2053
2054         CurrentMenu = MENU_MAIN;
2055         mutt_enter_command ();
2056         mutt_check_rescore (Context);
2057         if (option (OPTFORCEREDRAWINDEX))
2058           menu->redraw = REDRAW_FULL;
2059         unset_option (OPTFORCEREDRAWINDEX);
2060         unset_option (OPTFORCEREDRAWPAGER);
2061         break;
2062
2063       case OP_EDIT_MESSAGE:
2064
2065         CHECK_MSGCOUNT;
2066         CHECK_VISIBLE;
2067         CHECK_READONLY;
2068         CHECK_ATTACH;
2069
2070 #ifdef USE_POP
2071         if (Context->magic == M_POP)
2072         {
2073           mutt_flushinp ();
2074           mutt_error _("Can't edit message on POP server.");
2075           break;
2076         }
2077 #endif
2078
2079 #ifdef USE_IMAP
2080 CHECK_IMAP_ACL(IMAP_ACL_INSERT);
2081 #endif
2082
2083 #ifdef USE_NNTP
2084         if (Context->magic == M_NNTP)
2085         {
2086           mutt_flushinp ();
2087           mutt_error _("Can't edit message on newsserver.");
2088           break;
2089         }
2090 #endif
2091
2092         if (option (OPTPGPAUTODEC) && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2093           mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2094         mutt_edit_message (Context, tag ? NULL : CURHDR);
2095         menu->redraw = REDRAW_FULL;
2096
2097         break;
2098
2099       case OP_FORWARD_MESSAGE:
2100
2101         CHECK_MSGCOUNT;
2102         CHECK_VISIBLE;
2103         CHECK_ATTACH;
2104         ci_send_message (SENDFORWARD, NULL, NULL, Context, tag ? NULL : CURHDR);
2105         menu->redraw = REDRAW_FULL;
2106         break;
2107
2108
2109       case OP_FORGET_PASSPHRASE:
2110         crypt_forget_passphrase ();
2111         break;
2112
2113       case OP_GROUP_REPLY:
2114
2115         CHECK_MSGCOUNT;
2116         CHECK_VISIBLE;
2117         CHECK_ATTACH;
2118         ci_send_message (SENDREPLY|SENDGROUPREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
2119         menu->redraw = REDRAW_FULL;
2120         break;
2121
2122       case OP_LIST_REPLY:
2123
2124         CHECK_ATTACH;
2125         CHECK_MSGCOUNT;
2126         CHECK_VISIBLE;
2127         ci_send_message (SENDREPLY|SENDLISTREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
2128         menu->redraw = REDRAW_FULL;
2129         break;
2130
2131       case OP_MAIL:
2132
2133         CHECK_ATTACH;
2134         ci_send_message (0, NULL, NULL, Context, NULL);
2135         menu->redraw = REDRAW_FULL;
2136         break;
2137
2138       case OP_MAIL_KEY:
2139         if (!(WithCrypto & APPLICATION_PGP))
2140           break;
2141         CHECK_ATTACH;
2142         ci_send_message (SENDKEY, NULL, NULL, NULL, NULL);
2143         menu->redraw = REDRAW_FULL;
2144         break;
2145
2146       
2147       case OP_EXTRACT_KEYS:
2148         if (!WithCrypto)
2149           break;
2150         CHECK_MSGCOUNT;
2151         CHECK_VISIBLE;
2152         crypt_extract_keys_from_messages(tag ? NULL : CURHDR);
2153         menu->redraw = REDRAW_FULL;
2154         break;
2155
2156
2157       case OP_CHECK_TRADITIONAL:
2158         if (!(WithCrypto & APPLICATION_PGP))
2159           break;
2160         CHECK_MSGCOUNT; 
2161         CHECK_VISIBLE;
2162         if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED)) 
2163           mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2164       
2165         if (menu->menu == MENU_PAGER)
2166         {
2167           op = OP_DISPLAY_MESSAGE;
2168           continue;
2169         }
2170         break;
2171       
2172       case OP_PIPE:
2173
2174         CHECK_MSGCOUNT;
2175         CHECK_VISIBLE;
2176         mutt_pipe_message (tag ? NULL : CURHDR);
2177         MAYBE_REDRAW (menu->redraw);
2178         break;
2179
2180       case OP_PRINT:
2181
2182         CHECK_MSGCOUNT;
2183         CHECK_VISIBLE;
2184         mutt_print_message (tag ? NULL : CURHDR);
2185         break;
2186
2187       case OP_MAIN_READ_THREAD:
2188       case OP_MAIN_READ_SUBTHREAD:
2189
2190         CHECK_MSGCOUNT;
2191         CHECK_VISIBLE;
2192         CHECK_READONLY;
2193
2194 #ifdef USE_IMAP
2195 CHECK_IMAP_ACL(IMAP_ACL_SEEN);
2196 #endif
2197
2198         rc = mutt_thread_set_flag (CURHDR, M_READ, 1,
2199                                    op == OP_MAIN_READ_THREAD ? 0 : 1);
2200
2201         if (rc != -1)
2202         {
2203           if (option (OPTRESOLVE))
2204           {
2205             if ((menu->current = (op == OP_MAIN_READ_THREAD ? 
2206                                   mutt_next_thread (CURHDR) : mutt_next_subthread (CURHDR))) == -1)
2207               menu->current = menu->oldcurrent;
2208           }
2209           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2210         }
2211         break;
2212
2213       case OP_RECALL_MESSAGE:
2214
2215         CHECK_ATTACH;
2216         ci_send_message (SENDPOSTPONED, NULL, NULL, Context, NULL);
2217         menu->redraw = REDRAW_FULL;
2218         break;
2219
2220       case OP_RESEND:
2221       
2222         CHECK_ATTACH;
2223         CHECK_MSGCOUNT;
2224         CHECK_VISIBLE;
2225       
2226         if (tag)
2227         {
2228           for (j = 0; j < Context->vcount; j++)
2229           {
2230             if (Context->hdrs[Context->v2r[j]]->tagged)
2231               mutt_resend_message (NULL, Context, Context->hdrs[Context->v2r[j]]);
2232           }
2233         }
2234         else
2235           mutt_resend_message (NULL, Context, CURHDR);
2236       
2237         menu->redraw = REDRAW_FULL;
2238         break;
2239
2240 #ifdef USE_NNTP
2241       case OP_POST:
2242       case OP_FOLLOWUP:
2243       case OP_FORWARD_TO_GROUP:
2244
2245         CHECK_ATTACH;
2246         if (op != OP_FOLLOWUP || !CURHDR->env->followup_to ||
2247             mutt_strcasecmp (CURHDR->env->followup_to, "poster") ||
2248             query_quadoption (OPT_FOLLOWUPTOPOSTER,_("Reply by mail as poster prefers?")) != M_YES)
2249         {
2250           if (Context && Context->magic == M_NNTP &&
2251               !((NNTP_DATA *)Context->data)->allowed &&
2252               query_quadoption (OPT_TOMODERATED, _("Posting to this group not allowed, may be moderated. Continue?")) != M_YES)
2253             break;
2254           if (op == OP_POST)
2255             ci_send_message (SENDNEWS, NULL, NULL, Context, NULL);
2256           else
2257           {
2258             CHECK_MSGCOUNT;
2259             if (op == OP_FOLLOWUP)
2260               ci_send_message (SENDNEWS|SENDREPLY, NULL, NULL, Context,
2261                                tag ? NULL : CURHDR);
2262             else
2263               ci_send_message (SENDNEWS|SENDFORWARD, NULL, NULL, Context,
2264                                tag ? NULL : CURHDR);
2265           }
2266           menu->redraw = REDRAW_FULL;
2267           break;
2268         }
2269 #endif
2270       
2271       case OP_REPLY:
2272
2273         CHECK_ATTACH;
2274         CHECK_MSGCOUNT;
2275         CHECK_VISIBLE;
2276         ci_send_message (SENDREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
2277         menu->redraw = REDRAW_FULL;
2278         break;
2279
2280       case OP_SHELL_ESCAPE:
2281
2282         mutt_shell_escape ();
2283         MAYBE_REDRAW (menu->redraw);
2284         break;
2285
2286       case OP_TAG_THREAD:
2287       case OP_TAG_SUBTHREAD:
2288
2289         CHECK_MSGCOUNT;
2290         CHECK_VISIBLE;
2291         rc = mutt_thread_set_flag (CURHDR, M_TAG, !CURHDR->tagged,
2292                                    op == OP_TAG_THREAD ? 0 : 1);
2293         
2294         if (rc != -1)
2295         {
2296           if (option (OPTRESOLVE))
2297           {
2298             menu->current = mutt_next_thread (CURHDR);
2299
2300             if (menu->current == -1)
2301               menu->current = menu->oldcurrent;
2302           }
2303           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2304         }
2305         break;
2306
2307       case OP_UNDELETE:
2308
2309         CHECK_MSGCOUNT;
2310         CHECK_VISIBLE;
2311         CHECK_READONLY;
2312
2313 #ifdef USE_IMAP
2314 CHECK_IMAP_ACL(IMAP_ACL_DELETE);
2315 #endif
2316         
2317         if (tag)
2318         {
2319           mutt_tag_set_flag (M_DELETE, 0);
2320           mutt_tag_set_flag (M_PURGED, 0);
2321           menu->redraw = REDRAW_INDEX;
2322         }
2323         else
2324         {
2325           mutt_set_flag (Context, CURHDR, M_DELETE, 0);
2326           mutt_set_flag (Context, CURHDR, M_PURGED, 0);
2327           if (option (OPTRESOLVE) && menu->current < Context->vcount - 1)
2328           {
2329             menu->current++;
2330             menu->redraw = REDRAW_MOTION_RESYNCH;
2331           }
2332           else
2333             menu->redraw = REDRAW_CURRENT;
2334         }
2335         menu->redraw |= REDRAW_STATUS;
2336         break;
2337
2338       case OP_UNDELETE_THREAD:
2339       case OP_UNDELETE_SUBTHREAD:
2340
2341         CHECK_MSGCOUNT;
2342         CHECK_VISIBLE;
2343         CHECK_READONLY;
2344
2345 #ifdef USE_IMAP
2346 CHECK_IMAP_ACL(IMAP_ACL_DELETE);
2347 #endif
2348
2349         rc = mutt_thread_set_flag (CURHDR, M_DELETE, 0,
2350                                    op == OP_UNDELETE_THREAD ? 0 : 1)
2351           + mutt_thread_set_flag (CURHDR, M_PURGED, 0,
2352                                   op == OP_UNDELETE_THREAD ? 0 : 1);
2353
2354         if (rc > -1)
2355         {
2356           if (option (OPTRESOLVE))
2357           {
2358             if (op == OP_UNDELETE_THREAD)
2359               menu->current = mutt_next_thread (CURHDR);
2360             else
2361               menu->current = mutt_next_subthread (CURHDR);
2362
2363             if (menu->current == -1)
2364               menu->current = menu->oldcurrent;
2365           }
2366           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2367         }
2368         break;
2369
2370       case OP_VERSION:
2371         mutt_version ();
2372         break;
2373
2374       case OP_BUFFY_LIST:
2375         mutt_buffy_list ();
2376         break;
2377
2378       case OP_VIEW_ATTACHMENTS:
2379         CHECK_MSGCOUNT;
2380         CHECK_VISIBLE;
2381         mutt_view_attachments (CURHDR);
2382         if (CURHDR->attach_del)
2383           Context->changed = 1;
2384         menu->redraw = REDRAW_FULL;
2385         break;
2386
2387       case OP_END_COND:
2388         break;
2389
2390       case OP_WHAT_KEY:
2391         mutt_what_key();
2392         break;
2393
2394       case OP_SIDEBAR_SCROLL_UP:
2395       case OP_SIDEBAR_SCROLL_DOWN:
2396       case OP_SIDEBAR_NEXT:
2397       case OP_SIDEBAR_PREV:
2398       case OP_SIDEBAR_NEXT_NEW:
2399       case OP_SIDEBAR_PREV_NEW:
2400         scroll_sidebar(op, menu->menu);
2401             break;
2402       default:
2403         if (menu->menu == MENU_MAIN)
2404           km_error_key (MENU_MAIN);
2405     }
2406
2407     if (menu->menu == MENU_PAGER)
2408     {
2409       menu->menu = MENU_MAIN;
2410       menu->redraw = REDRAW_FULL;
2411 #if 0
2412       set_option (OPTWEED); /* turn header weeding back on. */
2413 #endif
2414     }
2415
2416     if (done) break;
2417   }
2418
2419 #ifdef USE_IMAP
2420   /* Close all open IMAP connections */
2421   if (!attach_msg)
2422     imap_logout_all ();
2423 #endif
2424 #ifdef USE_NNTP
2425   /* Close all open NNTP connections */
2426   if (!attach_msg)
2427     nntp_logout_all ();
2428 #endif
2429
2430   mutt_menuDestroy (&menu);
2431   return (close);
2432 }
2433
2434 void mutt_set_header_color (CONTEXT *ctx, HEADER *curhdr)
2435 {
2436   COLOR_LINE *color;
2437
2438   if (!curhdr)
2439     return;
2440   
2441   for (color = ColorIndexList; color; color = color->next)
2442    if (mutt_pattern_exec (color->color_pattern, M_MATCH_FULL_ADDRESS, ctx, curhdr))
2443    {
2444       curhdr->pair = color->pair;
2445       return;
2446    }
2447   curhdr->pair = ColorDefs[MT_COLOR_NORMAL];
2448 }