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
1302       if ((op = mutt_display_message (CURHDR)) == -1) {
1303         unset_option (OPTNEEDRESORT);
1304         break;
1305       }
1306
1307       menu->menu = MENU_PAGER;
1308       menu->oldcurrent = menu->current;
1309       continue;
1310
1311     case OP_EXIT:
1312
1313       close = op;
1314       if (menu->menu == MENU_MAIN && attach_msg) {
1315         done = 1;
1316         break;
1317       }
1318
1319       if ((menu->menu == MENU_MAIN)
1320           && (query_quadoption (OPT_QUIT,
1321                                 _("Exit Mutt-ng without saving?")) == M_YES))
1322       {
1323         if (Context) {
1324           mx_fastclose_mailbox (Context);
1325           FREE (&Context);
1326         }
1327         done = 1;
1328       }
1329       break;
1330
1331     case OP_EDIT_TYPE:
1332
1333       CHECK_MSGCOUNT;
1334       CHECK_VISIBLE;
1335       CHECK_ATTACH;
1336       mutt_edit_content_type (CURHDR, CURHDR->content, NULL);
1337       /* if we were in the pager, redisplay the message */
1338       if (menu->menu == MENU_PAGER) {
1339         op = OP_DISPLAY_MESSAGE;
1340         continue;
1341       }
1342       else
1343         menu->redraw = REDRAW_CURRENT;
1344       break;
1345
1346     case OP_MAIN_BREAK_THREAD:
1347
1348       CHECK_MSGCOUNT;
1349       CHECK_VISIBLE;
1350       CHECK_READONLY;
1351
1352       if ((Sort & SORT_MASK) != SORT_THREADS)
1353         mutt_error (_("Threading is not enabled."));
1354
1355       else {
1356         {
1357           HEADER *oldcur = CURHDR;
1358
1359           mutt_break_thread (CURHDR);
1360           mutt_sort_headers (Context, 1);
1361           menu->current = oldcur->virtual;
1362         }
1363
1364         Context->changed = 1;
1365         mutt_message _("Thread broken");
1366
1367         if (menu->menu == MENU_PAGER) {
1368           op = OP_DISPLAY_MESSAGE;
1369           continue;
1370         }
1371         else
1372           menu->redraw |= REDRAW_INDEX;
1373       }
1374       break;
1375
1376     case OP_MAIN_LINK_THREADS:
1377
1378       CHECK_MSGCOUNT;
1379       CHECK_VISIBLE;
1380       CHECK_READONLY;
1381
1382       if ((Sort & SORT_MASK) != SORT_THREADS)
1383         mutt_error (_("Threading is not enabled."));
1384
1385       else if (!CURHDR->env->message_id)
1386         mutt_error (_("No Message-ID: header available to link thread"));
1387
1388       else if (!tag && (!Context->last_tag || !Context->last_tag->tagged))
1389         mutt_error (_("First, please tag a message to be linked here"));
1390
1391       else {
1392         HEADER *oldcur = CURHDR;
1393
1394         if (mutt_link_threads (CURHDR, tag ? NULL : Context->last_tag,
1395                                Context)) {
1396           mutt_sort_headers (Context, 1);
1397           menu->current = oldcur->virtual;
1398
1399           Context->changed = 1;
1400           mutt_message _("Threads linked");
1401         }
1402         else
1403           mutt_error (_("No thread linked"));
1404       }
1405
1406       if (menu->menu == MENU_PAGER) {
1407         op = OP_DISPLAY_MESSAGE;
1408         continue;
1409       }
1410       else
1411         menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
1412       break;
1413
1414     case OP_MAIN_NEXT_UNDELETED:
1415
1416       CHECK_MSGCOUNT;
1417       CHECK_VISIBLE;
1418       if (menu->current >= Context->vcount - 1) {
1419         if (menu->menu == MENU_MAIN)
1420           mutt_error (_("You are on the last message."));
1421
1422         break;
1423       }
1424       if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1425         menu->current = menu->oldcurrent;
1426         if (menu->menu == MENU_MAIN)
1427           mutt_error (_("No undeleted messages."));
1428       }
1429       else if (menu->menu == MENU_PAGER) {
1430         op = OP_DISPLAY_MESSAGE;
1431         continue;
1432       }
1433       else
1434         menu->redraw = REDRAW_MOTION;
1435       break;
1436
1437     case OP_NEXT_ENTRY:
1438
1439       CHECK_MSGCOUNT;
1440       CHECK_VISIBLE;
1441       if (menu->current >= Context->vcount - 1) {
1442         if (menu->menu == MENU_MAIN)
1443           mutt_error (_("You are on the last message."));
1444
1445         break;
1446       }
1447       menu->current++;
1448       if (menu->menu == MENU_PAGER) {
1449         op = OP_DISPLAY_MESSAGE;
1450         continue;
1451       }
1452       else
1453         menu->redraw = REDRAW_MOTION;
1454       break;
1455
1456     case OP_MAIN_PREV_UNDELETED:
1457
1458       CHECK_MSGCOUNT;
1459       CHECK_VISIBLE;
1460       if (menu->current < 1) {
1461         mutt_error (_("You are on the first message."));
1462
1463         break;
1464       }
1465       if ((menu->current = ci_previous_undeleted (menu->current)) == -1) {
1466         menu->current = menu->oldcurrent;
1467         if (menu->menu == MENU_MAIN)
1468           mutt_error (_("No undeleted messages."));
1469       }
1470       else if (menu->menu == MENU_PAGER) {
1471         op = OP_DISPLAY_MESSAGE;
1472         continue;
1473       }
1474       else
1475         menu->redraw = REDRAW_MOTION;
1476       break;
1477
1478     case OP_PREV_ENTRY:
1479
1480       CHECK_MSGCOUNT;
1481       CHECK_VISIBLE;
1482       if (menu->current < 1) {
1483         if (menu->menu == MENU_MAIN)
1484           mutt_error (_("You are on the first message."));
1485
1486         break;
1487       }
1488       menu->current--;
1489       if (menu->menu == MENU_PAGER) {
1490         op = OP_DISPLAY_MESSAGE;
1491         continue;
1492       }
1493       else
1494         menu->redraw = REDRAW_MOTION;
1495       break;
1496
1497     case OP_DECRYPT_COPY:
1498     case OP_DECRYPT_SAVE:
1499       if (!WithCrypto)
1500         break;
1501       /* fall thru */
1502     case OP_COPY_MESSAGE:
1503     case OP_SAVE:
1504     case OP_DECODE_COPY:
1505     case OP_DECODE_SAVE:
1506       CHECK_MSGCOUNT;
1507       CHECK_VISIBLE;
1508       if (mutt_save_message (tag ? NULL : CURHDR,
1509                              (op == OP_DECRYPT_SAVE) ||
1510                              (op == OP_SAVE) || (op == OP_DECODE_SAVE),
1511                              (op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY),
1512                              (op == OP_DECRYPT_SAVE)
1513                              || (op == OP_DECRYPT_COPY)
1514                              || 0, &menu->redraw) == 0 && (op == OP_SAVE
1515                                                            || op ==
1516                                                            OP_DECODE_SAVE
1517                                                            || op ==
1518                                                            OP_DECRYPT_SAVE)
1519         ) {
1520         if (tag)
1521           menu->redraw |= REDRAW_INDEX;
1522         else if (option (OPTRESOLVE)) {
1523           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1524             menu->current = menu->oldcurrent;
1525             menu->redraw |= REDRAW_CURRENT;
1526           }
1527           else
1528             menu->redraw |= REDRAW_MOTION_RESYNCH;
1529         }
1530         else
1531           menu->redraw |= REDRAW_CURRENT;
1532       }
1533       break;
1534
1535     case OP_MAIN_NEXT_NEW:
1536     case OP_MAIN_NEXT_UNREAD:
1537     case OP_MAIN_PREV_NEW:
1538     case OP_MAIN_PREV_UNREAD:
1539     case OP_MAIN_NEXT_NEW_THEN_UNREAD:
1540     case OP_MAIN_PREV_NEW_THEN_UNREAD:
1541
1542       {
1543         int first_unread = -1;
1544         int first_new = -1;
1545
1546         CHECK_MSGCOUNT;
1547         CHECK_VISIBLE;
1548
1549         i = menu->current;
1550         menu->current = -1;
1551         for (j = 0; j != Context->vcount; j++) {
1552 #define CURHDRi Context->hdrs[Context->v2r[i]]
1553           if (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_NEXT_UNREAD
1554               || op == OP_MAIN_NEXT_NEW_THEN_UNREAD) {
1555             i++;
1556             if (i > Context->vcount - 1) {
1557               mutt_message _("Search wrapped to top.");
1558
1559               i = 0;
1560             }
1561           }
1562           else {
1563             i--;
1564             if (i < 0) {
1565               mutt_message _("Search wrapped to bottom.");
1566
1567               i = Context->vcount - 1;
1568             }
1569           }
1570
1571           if (CURHDRi->collapsed && (Sort & SORT_MASK) == SORT_THREADS) {
1572             if (UNREAD (CURHDRi) && first_unread == -1)
1573               first_unread = i;
1574             if (UNREAD (CURHDRi) == 1 && first_new == -1)
1575               first_new = i;
1576           }
1577           else if ((!CURHDRi->deleted && !CURHDRi->read)) {
1578             if (first_unread == -1)
1579               first_unread = i;
1580             if ((!CURHDRi->old) && first_new == -1)
1581               first_new = i;
1582           }
1583
1584           if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD) &&
1585               first_unread != -1)
1586             break;
1587           if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1588                op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1589                || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1590               && first_new != -1)
1591             break;
1592         }
1593 #undef CURHDRi
1594         if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1595              op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1596              || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1597             && first_new != -1)
1598           menu->current = first_new;
1599         else if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD ||
1600                   op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1601                   || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1602                  && first_unread != -1)
1603           menu->current = first_unread;
1604
1605         if (menu->current == -1) {
1606           menu->current = menu->oldcurrent;
1607           mutt_error ("%s%s.",
1608                       (op == OP_MAIN_NEXT_NEW
1609                        || op ==
1610                        OP_MAIN_PREV_NEW) ? _("No new messages") :
1611                       _("No unread messages"),
1612                       Context->pattern ? _(" in this limited view") : "");
1613         }
1614         else if (menu->menu == MENU_PAGER) {
1615           op = OP_DISPLAY_MESSAGE;
1616           continue;
1617         }
1618         else
1619           menu->redraw = REDRAW_MOTION;
1620         break;
1621       }
1622     case OP_FLAG_MESSAGE:
1623
1624       CHECK_MSGCOUNT;
1625       CHECK_VISIBLE;
1626       CHECK_READONLY;
1627
1628       CHECK_MX_ACL (Context, ACL_WRITE, _("Flagging"));
1629
1630       if (tag) {
1631         for (j = 0; j < Context->vcount; j++) {
1632           if (Context->hdrs[Context->v2r[j]]->tagged)
1633             mutt_set_flag (Context, Context->hdrs[Context->v2r[j]],
1634                            M_FLAG, !Context->hdrs[Context->v2r[j]]->flagged);
1635         }
1636
1637         menu->redraw |= REDRAW_INDEX;
1638       }
1639       else {
1640         mutt_set_flag (Context, CURHDR, M_FLAG, !CURHDR->flagged);
1641         if (option (OPTRESOLVE)) {
1642           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1643             menu->current = menu->oldcurrent;
1644             menu->redraw = REDRAW_CURRENT;
1645           }
1646           else
1647             menu->redraw = REDRAW_MOTION_RESYNCH;
1648         }
1649         else
1650           menu->redraw = REDRAW_CURRENT;
1651       }
1652       menu->redraw |= REDRAW_STATUS;
1653       break;
1654
1655     case OP_TOGGLE_NEW:
1656
1657       CHECK_MSGCOUNT;
1658       CHECK_VISIBLE;
1659       CHECK_READONLY;
1660
1661       CHECK_MX_ACL (Context, ACL_SEEN, _("Toggling"));
1662
1663       if (tag) {
1664         for (j = 0; j < Context->vcount; j++) {
1665           if (Context->hdrs[Context->v2r[j]]->tagged) {
1666             if (Context->hdrs[Context->v2r[j]]->read ||
1667                 Context->hdrs[Context->v2r[j]]->old)
1668               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_NEW,
1669                              1);
1670             else
1671               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_READ,
1672                              1);
1673           }
1674         }
1675         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1676       }
1677       else {
1678         if (CURHDR->read || CURHDR->old)
1679           mutt_set_flag (Context, CURHDR, M_NEW, 1);
1680         else
1681           mutt_set_flag (Context, CURHDR, M_READ, 1);
1682
1683         if (option (OPTRESOLVE)) {
1684           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1685             menu->current = menu->oldcurrent;
1686             menu->redraw = REDRAW_CURRENT;
1687           }
1688           else
1689             menu->redraw = REDRAW_MOTION_RESYNCH;
1690         }
1691         else
1692           menu->redraw = REDRAW_CURRENT;
1693         menu->redraw |= REDRAW_STATUS;
1694       }
1695       break;
1696
1697     case OP_TOGGLE_WRITE:
1698
1699       CHECK_MSGCOUNT;
1700       if (mx_toggle_write (Context) == 0)
1701         menu->redraw |= REDRAW_STATUS;
1702       break;
1703
1704     case OP_MAIN_NEXT_THREAD:
1705     case OP_MAIN_NEXT_SUBTHREAD:
1706     case OP_MAIN_PREV_THREAD:
1707     case OP_MAIN_PREV_SUBTHREAD:
1708
1709       CHECK_MSGCOUNT;
1710       CHECK_VISIBLE;
1711       switch (op) {
1712       case OP_MAIN_NEXT_THREAD:
1713         menu->current = mutt_next_thread (CURHDR);
1714         break;
1715
1716       case OP_MAIN_NEXT_SUBTHREAD:
1717         menu->current = mutt_next_subthread (CURHDR);
1718         break;
1719
1720       case OP_MAIN_PREV_THREAD:
1721         menu->current = mutt_previous_thread (CURHDR);
1722         break;
1723
1724       case OP_MAIN_PREV_SUBTHREAD:
1725         menu->current = mutt_previous_subthread (CURHDR);
1726         break;
1727       }
1728
1729       if (menu->current < 0) {
1730         menu->current = menu->oldcurrent;
1731         if (op == OP_MAIN_NEXT_THREAD || op == OP_MAIN_NEXT_SUBTHREAD)
1732           mutt_error (_("No more threads."));
1733
1734         else
1735           mutt_error (_("You are on the first thread."));
1736       }
1737       else if (menu->menu == MENU_PAGER) {
1738         op = OP_DISPLAY_MESSAGE;
1739         continue;
1740       }
1741       else
1742         menu->redraw = REDRAW_MOTION;
1743       break;
1744
1745     case OP_MAIN_PARENT_MESSAGE:
1746
1747       CHECK_MSGCOUNT;
1748       CHECK_VISIBLE;
1749
1750       if ((menu->current = mutt_parent_message (Context, CURHDR)) < 0) {
1751         menu->current = menu->oldcurrent;
1752       }
1753       else if (menu->menu == MENU_PAGER) {
1754         op = OP_DISPLAY_MESSAGE;
1755         continue;
1756       }
1757       else
1758         menu->redraw = REDRAW_MOTION;
1759       break;
1760
1761     case OP_MAIN_SET_FLAG:
1762     case OP_MAIN_CLEAR_FLAG:
1763
1764       CHECK_MSGCOUNT;
1765       CHECK_VISIBLE;
1766       CHECK_READONLY;
1767
1768       if (mutt_change_flag (tag ? NULL : CURHDR, (op == OP_MAIN_SET_FLAG)) ==
1769           0) {
1770         menu->redraw = REDRAW_STATUS;
1771         if (tag)
1772           menu->redraw |= REDRAW_INDEX;
1773         else if (option (OPTRESOLVE)) {
1774           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1775             menu->current = menu->oldcurrent;
1776             menu->redraw |= REDRAW_CURRENT;
1777           }
1778           else
1779             menu->redraw |= REDRAW_MOTION_RESYNCH;
1780         }
1781         else
1782           menu->redraw |= REDRAW_CURRENT;
1783       }
1784       break;
1785
1786     case OP_MAIN_COLLAPSE_THREAD:
1787       CHECK_MSGCOUNT;
1788       CHECK_VISIBLE;
1789
1790       if ((Sort & SORT_MASK) != SORT_THREADS) {
1791         mutt_error (_("Threading is not enabled."));
1792
1793         break;
1794       }
1795
1796       if (CURHDR->collapsed) {
1797         menu->current = mutt_uncollapse_thread (Context, CURHDR);
1798         mutt_set_virtual (Context);
1799         if (option (OPTUNCOLLAPSEJUMP))
1800           menu->current = mutt_thread_next_unread (Context, CURHDR);
1801       }
1802       else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR)) {
1803         menu->current = mutt_collapse_thread (Context, CURHDR);
1804         mutt_set_virtual (Context);
1805       }
1806       else {
1807         mutt_error (_("Thread contains unread messages."));
1808
1809         break;
1810       }
1811
1812       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1813
1814       break;
1815
1816     case OP_MAIN_COLLAPSE_ALL:
1817       CHECK_MSGCOUNT;
1818       CHECK_VISIBLE;
1819
1820       if ((Sort & SORT_MASK) != SORT_THREADS) {
1821         mutt_error (_("Threading is not enabled."));
1822
1823         break;
1824       }
1825
1826       {
1827         HEADER *h, *base;
1828         THREAD *thread, *top;
1829         int final;
1830
1831         if (CURHDR->collapsed)
1832           final = mutt_uncollapse_thread (Context, CURHDR);
1833         else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR))
1834           final = mutt_collapse_thread (Context, CURHDR);
1835         else
1836           final = CURHDR->virtual;
1837
1838         base = Context->hdrs[Context->v2r[final]];
1839
1840         top = Context->tree;
1841         Context->collapsed = !Context->collapsed;
1842         while ((thread = top) != NULL) {
1843           while (!thread->message)
1844             thread = thread->child;
1845           h = thread->message;
1846
1847           if (h->collapsed != Context->collapsed) {
1848             if (h->collapsed)
1849               mutt_uncollapse_thread (Context, h);
1850             else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (h))
1851               mutt_collapse_thread (Context, h);
1852           }
1853           top = top->next;
1854         }
1855
1856         mutt_set_virtual (Context);
1857         for (j = 0; j < Context->vcount; j++) {
1858           if (Context->hdrs[Context->v2r[j]]->index == base->index) {
1859             menu->current = j;
1860             break;
1861           }
1862         }
1863
1864         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1865       }
1866       break;
1867
1868       /* --------------------------------------------------------------------
1869        * These functions are invoked directly from the internal-pager
1870        */
1871
1872     case OP_BOUNCE_MESSAGE:
1873
1874       CHECK_ATTACH;
1875       CHECK_MSGCOUNT;
1876       CHECK_VISIBLE;
1877       ci_bounce_message (tag ? NULL : CURHDR, &menu->redraw);
1878       break;
1879
1880     case OP_CREATE_ALIAS:
1881
1882       mutt_create_alias (Context
1883                          && Context->vcount ? CURHDR->env : NULL, NULL);
1884       MAYBE_REDRAW (menu->redraw);
1885       menu->redraw |= REDRAW_CURRENT;
1886       break;
1887
1888     case OP_QUERY:
1889       CHECK_ATTACH;
1890       mutt_query_menu (NULL, 0);
1891       MAYBE_REDRAW (menu->redraw);
1892       break;
1893
1894     case OP_PURGE_MESSAGE:
1895     case OP_DELETE:
1896
1897       CHECK_MSGCOUNT;
1898       CHECK_VISIBLE;
1899       CHECK_READONLY;
1900
1901       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
1902
1903       if (tag) {
1904         mutt_tag_set_flag (M_DELETE, 1);
1905         mutt_tag_set_flag (M_PURGED, (op != OP_PURGE_MESSAGE) ? 0 : 1);
1906         if (option (OPTDELETEUNTAG))
1907           mutt_tag_set_flag (M_TAG, 0);
1908         menu->redraw = REDRAW_INDEX;
1909       }
1910       else {
1911         mutt_set_flag (Context, CURHDR, M_DELETE, 1);
1912         mutt_set_flag (Context, CURHDR, M_PURGED,
1913                        (op != OP_PURGE_MESSAGE) ? 0 : 1);
1914         if (option (OPTDELETEUNTAG))
1915           mutt_set_flag (Context, CURHDR, M_TAG, 0);
1916         if (option (OPTRESOLVE)) {
1917           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1918             menu->current = menu->oldcurrent;
1919             menu->redraw = REDRAW_CURRENT;
1920           }
1921           else if (menu->menu == MENU_PAGER) {
1922             op = OP_DISPLAY_MESSAGE;
1923             continue;
1924           }
1925           else
1926             menu->redraw |= REDRAW_MOTION_RESYNCH;
1927         }
1928         else
1929           menu->redraw = REDRAW_CURRENT;
1930       }
1931       menu->redraw |= REDRAW_STATUS;
1932       break;
1933
1934     case OP_DELETE_THREAD:
1935     case OP_DELETE_SUBTHREAD:
1936
1937       CHECK_MSGCOUNT;
1938       CHECK_VISIBLE;
1939       CHECK_READONLY;
1940
1941       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
1942
1943       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 1,
1944                                  op == OP_DELETE_THREAD ? 0 : 1);
1945
1946       if (rc != -1) {
1947         if (option (OPTDELETEUNTAG))
1948           mutt_thread_set_flag (CURHDR, M_TAG, 0,
1949                                 op == OP_DELETE_THREAD ? 0 : 1);
1950         if (option (OPTRESOLVE))
1951           if ((menu->current = ci_next_undeleted (menu->current)) == -1)
1952             menu->current = menu->oldcurrent;
1953         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1954       }
1955       break;
1956
1957 #ifdef USE_NNTP
1958     case OP_CATCHUP:
1959       if (Context && Context->magic == M_NNTP) {
1960         if (mutt_newsgroup_catchup (CurrentNewsSrv,
1961                                     ((NNTP_DATA *) Context->data)->group))
1962           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1963       }
1964       break;
1965 #endif
1966
1967     case OP_DISPLAY_ADDRESS:
1968
1969       CHECK_MSGCOUNT;
1970       CHECK_VISIBLE;
1971       mutt_display_address (CURHDR->env);
1972       break;
1973
1974     case OP_ENTER_COMMAND:
1975
1976       CurrentMenu = MENU_MAIN;
1977       mutt_enter_command ();
1978       mutt_check_rescore (Context);
1979       if (option (OPTFORCEREDRAWINDEX))
1980         menu->redraw = REDRAW_FULL;
1981       unset_option (OPTFORCEREDRAWINDEX);
1982       unset_option (OPTFORCEREDRAWPAGER);
1983       break;
1984
1985     case OP_EDIT_MESSAGE:
1986
1987       CHECK_MSGCOUNT;
1988       CHECK_VISIBLE;
1989       CHECK_READONLY;
1990       CHECK_ATTACH;
1991
1992       CHECK_MX_ACL (Context, ACL_INSERT, _("Editing"));
1993
1994       if (option (OPTPGPAUTODEC)
1995           && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED)))
1996         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1997       mutt_edit_message (Context, tag ? NULL : CURHDR);
1998       menu->redraw = REDRAW_FULL;
1999
2000       break;
2001
2002     case OP_FORWARD_MESSAGE:
2003
2004       CHECK_MSGCOUNT;
2005       CHECK_VISIBLE;
2006       CHECK_ATTACH;
2007
2008       if (option (OPTPGPAUTODEC) &&
2009           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2010         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2011       ci_send_message (SENDFORWARD, NULL, NULL, Context, tag ? NULL : CURHDR);
2012       menu->redraw = REDRAW_FULL;
2013       break;
2014
2015
2016     case OP_FORGET_PASSPHRASE:
2017       crypt_forget_passphrase ();
2018       break;
2019
2020     case OP_GROUP_REPLY:
2021
2022       CHECK_MSGCOUNT;
2023       CHECK_VISIBLE;
2024       CHECK_ATTACH;
2025
2026       if (option (OPTPGPAUTODEC) &&
2027           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2028         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2029
2030       ci_send_message (SENDREPLY | SENDGROUPREPLY, NULL, NULL, Context,
2031                        tag ? NULL : CURHDR);
2032       menu->redraw = REDRAW_FULL;
2033       break;
2034
2035     case OP_LIST_REPLY:
2036
2037       CHECK_ATTACH;
2038       CHECK_MSGCOUNT;
2039       CHECK_VISIBLE;
2040
2041       if (option (OPTPGPAUTODEC) &&
2042           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2043         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2044
2045       ci_send_message (SENDREPLY | SENDLISTREPLY, NULL, NULL, Context,
2046                        tag ? NULL : CURHDR);
2047       menu->redraw = REDRAW_FULL;
2048       break;
2049
2050     case OP_MAIL:
2051
2052       CHECK_ATTACH;
2053       ci_send_message (0, NULL, NULL, Context, NULL);
2054       menu->redraw = REDRAW_FULL;
2055       break;
2056
2057     case OP_MAIL_KEY:
2058       if (!(WithCrypto & APPLICATION_PGP))
2059         break;
2060       CHECK_ATTACH;
2061       ci_send_message (SENDKEY, NULL, NULL, NULL, NULL);
2062       menu->redraw = REDRAW_FULL;
2063       break;
2064
2065
2066     case OP_EXTRACT_KEYS:
2067       if (!WithCrypto)
2068         break;
2069       CHECK_MSGCOUNT;
2070       CHECK_VISIBLE;
2071       crypt_extract_keys_from_messages (tag ? NULL : CURHDR);
2072       menu->redraw = REDRAW_FULL;
2073       break;
2074
2075
2076     case OP_CHECK_TRADITIONAL:
2077       if (!(WithCrypto & APPLICATION_PGP))
2078         break;
2079       CHECK_MSGCOUNT;
2080       CHECK_VISIBLE;
2081       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
2082         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2083
2084       if (menu->menu == MENU_PAGER) {
2085         op = OP_DISPLAY_MESSAGE;
2086         continue;
2087       }
2088       break;
2089
2090     case OP_PIPE:
2091
2092       CHECK_MSGCOUNT;
2093       CHECK_VISIBLE;
2094       mutt_pipe_message (tag ? NULL : CURHDR);
2095       MAYBE_REDRAW (menu->redraw);
2096       break;
2097
2098     case OP_PRINT:
2099
2100       CHECK_MSGCOUNT;
2101       CHECK_VISIBLE;
2102       mutt_print_message (tag ? NULL : CURHDR);
2103       break;
2104
2105     case OP_MAIN_READ_THREAD:
2106     case OP_MAIN_READ_SUBTHREAD:
2107
2108       CHECK_MSGCOUNT;
2109       CHECK_VISIBLE;
2110       CHECK_READONLY;
2111
2112       CHECK_MX_ACL (Context, ACL_SEEN, _("Marking as read"));
2113
2114       rc = mutt_thread_set_flag (CURHDR, M_READ, 1,
2115                                  op == OP_MAIN_READ_THREAD ? 0 : 1);
2116
2117       if (rc != -1) {
2118         if (option (OPTRESOLVE)) {
2119           if ((menu->current = (op == OP_MAIN_READ_THREAD ?
2120                                 mutt_next_thread (CURHDR) :
2121                                 mutt_next_subthread (CURHDR))) == -1)
2122             menu->current = menu->oldcurrent;
2123         }
2124         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2125       }
2126       break;
2127
2128     case OP_RECALL_MESSAGE:
2129
2130       CHECK_ATTACH;
2131       ci_send_message (SENDPOSTPONED, NULL, NULL, Context, NULL);
2132       menu->redraw = REDRAW_FULL;
2133       break;
2134
2135     case OP_RESEND:
2136
2137       CHECK_ATTACH;
2138       CHECK_MSGCOUNT;
2139       CHECK_VISIBLE;
2140
2141       if (tag) {
2142         for (j = 0; j < Context->vcount; j++) {
2143           if (Context->hdrs[Context->v2r[j]]->tagged)
2144             mutt_resend_message (NULL, Context,
2145                                  Context->hdrs[Context->v2r[j]]);
2146         }
2147       }
2148       else
2149         mutt_resend_message (NULL, Context, CURHDR);
2150
2151       menu->redraw = REDRAW_FULL;
2152       break;
2153
2154 #ifdef USE_NNTP
2155     case OP_POST:
2156     case OP_FOLLOWUP:
2157     case OP_FORWARD_TO_GROUP:
2158
2159       CHECK_ATTACH;
2160       if ((op == OP_FOLLOWUP || op == OP_FORWARD_TO_GROUP) &&
2161           Context && Context->msgcount == 0) {
2162         mutt_error (_("There are no messages."));
2163         sleep (2);
2164       }
2165       else if (op != OP_FOLLOWUP || !CURHDR->env->followup_to ||
2166                safe_strcasecmp (CURHDR->env->followup_to, "poster") ||
2167                query_quadoption (OPT_FOLLOWUPTOPOSTER,
2168                                  _("Reply by mail as poster prefers?")) !=
2169                M_YES) {
2170         if (Context && Context->magic == M_NNTP
2171             && !((NNTP_DATA *) Context->data)->allowed
2172             && query_quadoption (OPT_TOMODERATED,
2173                                  _
2174                                  ("Posting to this group not allowed, may be moderated. Continue?"))
2175             != M_YES)
2176           break;
2177         if (op == OP_POST)
2178           ci_send_message (SENDNEWS, NULL, NULL, Context, NULL);
2179         else {
2180           CHECK_MSGCOUNT;
2181           if (op == OP_FOLLOWUP)
2182             ci_send_message (SENDNEWS | SENDREPLY, NULL, NULL, Context,
2183                              tag ? NULL : CURHDR);
2184           else
2185             ci_send_message (SENDNEWS | SENDFORWARD, NULL, NULL, Context,
2186                              tag ? NULL : CURHDR);
2187         }
2188         menu->redraw = REDRAW_FULL;
2189         break;
2190       }
2191 #endif
2192
2193     case OP_REPLY:
2194
2195       CHECK_ATTACH;
2196       CHECK_MSGCOUNT;
2197       CHECK_VISIBLE;
2198
2199       if (option (OPTPGPAUTODEC) &&
2200           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2201         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2202
2203       ci_send_message (SENDREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
2204       menu->redraw = REDRAW_FULL;
2205       break;
2206
2207     case OP_SHELL_ESCAPE:
2208
2209       mutt_shell_escape ();
2210       MAYBE_REDRAW (menu->redraw);
2211       break;
2212
2213     case OP_TAG_THREAD:
2214     case OP_TAG_SUBTHREAD:
2215
2216       CHECK_MSGCOUNT;
2217       CHECK_VISIBLE;
2218       rc = mutt_thread_set_flag (CURHDR, M_TAG, !CURHDR->tagged,
2219                                  op == OP_TAG_THREAD ? 0 : 1);
2220
2221       if (rc != -1) {
2222         if (option (OPTRESOLVE)) {
2223           menu->current = mutt_next_thread (CURHDR);
2224
2225           if (menu->current == -1)
2226             menu->current = menu->oldcurrent;
2227         }
2228         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2229       }
2230       break;
2231
2232     case OP_UNDELETE:
2233
2234       CHECK_MSGCOUNT;
2235       CHECK_VISIBLE;
2236       CHECK_READONLY;
2237
2238       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
2239
2240       if (tag) {
2241         mutt_tag_set_flag (M_DELETE, 0);
2242         mutt_tag_set_flag (M_PURGED, 0);
2243         menu->redraw = REDRAW_INDEX;
2244       }
2245       else {
2246         mutt_set_flag (Context, CURHDR, M_DELETE, 0);
2247         mutt_set_flag (Context, CURHDR, M_PURGED, 0);
2248         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
2249           menu->current++;
2250           menu->redraw = REDRAW_MOTION_RESYNCH;
2251         }
2252         else
2253           menu->redraw = REDRAW_CURRENT;
2254       }
2255       menu->redraw |= REDRAW_STATUS;
2256       break;
2257
2258     case OP_UNDELETE_THREAD:
2259     case OP_UNDELETE_SUBTHREAD:
2260
2261       CHECK_MSGCOUNT;
2262       CHECK_VISIBLE;
2263       CHECK_READONLY;
2264
2265       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
2266
2267       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 0,
2268                                  op == OP_UNDELETE_THREAD ? 0 : 1)
2269         + mutt_thread_set_flag (CURHDR, M_PURGED, 0,
2270                                 op == OP_UNDELETE_THREAD ? 0 : 1);
2271
2272       if (rc > -1) {
2273         if (option (OPTRESOLVE)) {
2274           if (op == OP_UNDELETE_THREAD)
2275             menu->current = mutt_next_thread (CURHDR);
2276           else
2277             menu->current = mutt_next_subthread (CURHDR);
2278
2279           if (menu->current == -1)
2280             menu->current = menu->oldcurrent;
2281         }
2282         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2283       }
2284       break;
2285
2286     case OP_VERSION:
2287       mutt_version ();
2288       break;
2289
2290     case OP_BUFFY_LIST:
2291       buffy_list ();
2292       menu->redraw = REDRAW_FULL;
2293       break;
2294
2295     case OP_VIEW_ATTACHMENTS:
2296       CHECK_MSGCOUNT;
2297       CHECK_VISIBLE;
2298       mutt_view_attachments (CURHDR);
2299       if (CURHDR->attach_del)
2300         Context->changed = 1;
2301       menu->redraw = REDRAW_FULL;
2302       break;
2303
2304     case OP_END_COND:
2305       break;
2306
2307     case OP_WHAT_KEY:
2308       mutt_what_key ();
2309       break;
2310
2311     case OP_SIDEBAR_SCROLL_UP:
2312     case OP_SIDEBAR_SCROLL_DOWN:
2313     case OP_SIDEBAR_NEXT:
2314     case OP_SIDEBAR_PREV:
2315     case OP_SIDEBAR_NEXT_NEW:
2316     case OP_SIDEBAR_PREV_NEW:
2317       sidebar_scroll (op, menu->menu);
2318       break;
2319     default:
2320       if (menu->menu == MENU_MAIN)
2321         km_error_key (MENU_MAIN);
2322     }
2323
2324     if (menu->menu == MENU_PAGER) {
2325       menu->menu = MENU_MAIN;
2326       menu->redraw = REDRAW_FULL;
2327 #if 0
2328       set_option (OPTWEED);     /* turn header weeding back on. */
2329 #endif
2330     }
2331
2332     if (done)
2333       break;
2334   }
2335
2336 #ifdef USE_IMAP
2337   /* Close all open IMAP connections */
2338   if (!attach_msg)
2339     imap_logout_all ();
2340 #endif
2341 #ifdef USE_NNTP
2342   /* Close all open NNTP connections */
2343   if (!attach_msg)
2344     nntp_logout_all ();
2345 #endif
2346
2347   mutt_menuDestroy (&menu);
2348   return (close);
2349 }
2350
2351 void mutt_set_header_color (CONTEXT * ctx, HEADER * curhdr)
2352 {
2353   COLOR_LINE *color;
2354
2355   if (!curhdr)
2356     return;
2357
2358   for (color = ColorIndexList; color; color = color->next)
2359     if (mutt_pattern_exec
2360         (color->color_pattern, M_MATCH_FULL_ADDRESS, ctx, curhdr)) {
2361       curhdr->pair = color->pair;
2362       return;
2363     }
2364   curhdr->pair = ColorDefs[MT_COLOR_NORMAL];
2365 }