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