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