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