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