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