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