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