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