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