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