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