Move score_* to mod_score
[apps/madmutt.git] / lib-ui / curs_main.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  *
5  * Parts were written/modified by:
6  * Nico Golde <nico@ngolde.de>
7  *
8  * This file is part of mutt-ng, see http://www.muttng.org/.
9  * It's licensed under the GNU General Public License,
10  * please see the file GPL in the top level source directory.
11  */
12
13 #include <lib-lib/lib-lib.h>
14
15 #include <lib-ui/sidebar.h>
16 #include <lib-mx/mx.h>
17 #include "pop.h"
18
19 #include "curses.h"
20 #include "menu.h"
21
22 #include "mutt.h"
23 #include "crypt.h"
24 #include "pattern.h"
25 #include "alias.h"
26 #include "sort.h"
27 #include "recvattach.h"
28 #include "buffy.h"
29 #include "thread.h"
30 #include "score.h"
31
32 #include <imap/imap_private.h>
33
34 #ifdef USE_NNTP
35 #include <nntp/nntp.h>
36 #endif
37
38 static const char *No_mailbox_is_open = N_("No mailbox is open.");
39 static const char *There_are_no_messages = N_("There are no messages.");
40 static const char *Mailbox_is_read_only = N_("Mailbox is read-only.");
41 static const char *Function_not_permitted_in_attach_message_mode =
42 N_("Function not permitted in attach-message mode.");
43 static const char *No_visible = N_("No visible messages.");
44
45 #define CHECK_IN_MAILBOX if (!Context) \
46         { \
47                 mutt_flushinp (); \
48                 mutt_error (_(No_mailbox_is_open)); \
49                 break; \
50         }
51
52 #define CHECK_MSGCOUNT if (!Context) \
53         { \
54                 mutt_flushinp (); \
55                 mutt_error(_(No_mailbox_is_open)); \
56                 break; \
57         } \
58         else if (!Context->msgcount) \
59         { \
60                   mutt_flushinp (); \
61                 mutt_error(_(There_are_no_messages)); \
62                 break; \
63         }
64
65 #define CHECK_VISIBLE if (Context && menu->current >= Context->vcount) \
66           {\
67                   mutt_flushinp (); \
68                   mutt_error(_(No_visible)); \
69                   break; \
70         }
71
72
73 #define CHECK_READONLY if (Context->readonly) \
74                         { \
75                                   mutt_flushinp (); \
76                                 mutt_error(_(Mailbox_is_read_only)); \
77                                 break; \
78                         }
79
80 #define CHECK_ATTACH if(option(OPTATTACHMSG)) \
81                      {\
82                         mutt_flushinp (); \
83                         mutt_error(_(Function_not_permitted_in_attach_message_mode)); \
84                         break; \
85                      }
86
87 #define CURHDR Context->hdrs[Context->v2r[menu->current]]
88 #define OLDHDR Context->hdrs[Context->v2r[menu->oldcurrent]]
89 #define UNREAD(h) mutt_thread_contains_unread (Context, h)
90 #define SW              (option(OPTMBOXPANE)?SidebarWidth:0)
91
92 extern size_t UngetCount;
93
94 void index_make_entry (char *s, ssize_t l, struct menu_t * menu, int num)
95 {
96   format_flag flag = M_FORMAT_MAKEPRINT | M_FORMAT_INDEX;
97   int edgemsgno, reverse = Sort & SORT_REVERSE;
98   HEADER *h = Context->hdrs[Context->v2r[num]];
99   THREAD *tmp;
100
101   if (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   const 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)) == 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_SIDEBAR_SCROLL_UP:
2285     case OP_SIDEBAR_SCROLL_DOWN:
2286     case OP_SIDEBAR_NEXT:
2287     case OP_SIDEBAR_PREV:
2288     case OP_SIDEBAR_NEXT_NEW:
2289     case OP_SIDEBAR_PREV_NEW:
2290       sidebar_scroll (op);
2291       break;
2292     default:
2293       if (menu->menu == MENU_MAIN)
2294         km_error_key (MENU_MAIN);
2295     }
2296
2297     if (menu->menu == MENU_PAGER) {
2298       menu->menu = MENU_MAIN;
2299       menu->redraw = REDRAW_FULL;
2300     }
2301
2302     if (done)
2303       break;
2304   }
2305
2306   if (!attach_msg) {
2307   /* Close all open IMAP connections */
2308     imap_logout_all ();
2309 #ifdef USE_NNTP
2310   /* Close all open NNTP connections */
2311     nntp_logout_all ();
2312 #endif
2313   }
2314
2315   mutt_menuDestroy (&menu);
2316   return (closed);
2317 }
2318
2319 void mutt_set_header_color (CONTEXT * ctx, HEADER * curhdr)
2320 {
2321   COLOR_LINE *color;
2322
2323   if (!curhdr)
2324     return;
2325
2326   for (color = ColorIndexList; color; color = color->next)
2327     if (mutt_pattern_exec
2328         (color->color_pattern, M_MATCH_FULL_ADDRESS, ctx, curhdr)) {
2329       curhdr->pair = color->pair;
2330       return;
2331     }
2332   curhdr->pair = ColorDefs[MT_COLOR_NORMAL];
2333 }