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