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