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