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