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