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