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