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