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