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