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