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