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